這是我最終的代碼(減去一些清理辦)的成功在仿真環境中測試:我用下面的代碼能夠在發展中存儲上傳的斑點。首先,客戶端代碼,它們是來自Microsoft REST API示例BlobHelper.cs的改編方法。然後是提供客戶端代碼使用的端點URL的服務器端代碼。再次感謝提示! [R
//
// Client side: The "Endpoint" used below is the Uri as returned from the Server-side code below.
//
//
public bool PutBlock(int blockId, string[] blockIds, byte[] value)
{
return Retry<bool>(delegate()
{
HttpWebResponse response;
try
{
SortedList<string, string> headers = new SortedList<string, string>();
byte[] blockIdBytes = BitConverter.GetBytes(blockId);
string blockIdBase64 = Convert.ToBase64String(blockIdBytes);
blockIds[blockId] = blockIdBase64;
// SharedAccessKey version.
//End result will look similar to this in Fiddler if correct:
//PUT http://127.0.0.1:10000/devstoreaccount1/uploads/aecdfa39-7eaa-474a-9333-ecf43e6a0508?st=2012-08-17T16%3A11%3A53Z&se=2012-08-17T16%3A51%3A53Z&sr=b&sp=rw&sig=2%2Fs0R1L78S55pW5o2WontVvlZypjkTriWoljnycPbFc%3D&comp=block&blockid=AAAAAA== HTTP/1.1
//
response = CreateRESTRequestDirectUtf8("PUT", "&comp=block&blockid=" + blockIdBase64, value, headers).GetResponse() as HttpWebResponse;
response.Close();
return true;
}
catch (WebException ex)
{
if (ex.Status == WebExceptionStatus.ProtocolError &&
ex.Response != null &&
(int)((HttpWebResponse)ex.Response).StatusCode == 409)
return false;
throw;
}
});
}
///<summary>
/// Put block list - complete creation of blob based on uploaded content.
/// </summary>
/// <param name="container">The container.</param>
/// <param name="blob">The BLOB.</param>
/// <param name="blockIds">The block ids.</param>
/// <returns></returns>
public bool PutBlockList(string[] blockIds)
{
return Retry<bool>(delegate()
{
HttpWebResponse response;
try
{
StringBuilder content = new StringBuilder();
content.Append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
content.Append("<BlockList>");
for (int i = 0; i < blockIds.Length; i++)
{
content.Append("<Latest>" + blockIds[i] + "</Latest>");
}
content.Append("</BlockList>");
response = CreateRESTRequest("PUT", "&comp=blocklist", content.ToString(), null).GetResponse() as HttpWebResponse;
response.Close();
return true;
}
catch (WebException ex)
{
if (ex.Status == WebExceptionStatus.ProtocolError &&
ex.Response != null &&
(int)(ex.Response as HttpWebResponse).StatusCode == 409)
return false;
throw;
}
});
}
/// <summary>
/// Construct and issue a REST request and return the response.
/// </summary>
/// <param name="method">The method.</param>
/// <param name="resource">The resource.</param>
/// <param name="requestBody">The request body.</param>
/// <param name="headers">The headers.</param>
/// <param name="ifMatch">If match.</param>
/// <param name="md5">The MD5.</param>
/// <returns></returns>
public HttpWebRequest CreateRESTRequest(string method, string resource, string requestBody = null, SortedList<string, string> headers = null,
string ifMatch = "", string md5 = "")
{
byte[] byteArray = null;
DateTime now = DateTime.UtcNow;
Uri uri = new Uri(Endpoint + resource);
HttpWebRequest request = HttpWebRequest.Create(uri) as HttpWebRequest;
request.Method = method;
request.ContentLength = 0;
request.Headers.Add("x-ms-date", now.ToString("R", System.Globalization.CultureInfo.InvariantCulture));
request.Headers.Add("x-ms-version", "2009-09-19"); //2009-09-19, 2011-08-18
if (IsTableStorage)
{
request.ContentType = "application/atom+xml";
request.Headers.Add("DataServiceVersion", "1.0;NetFx");
request.Headers.Add("MaxDataServiceVersion", "1.0;NetFx");
}
if (headers != null)
{
foreach (KeyValuePair<string, string> header in headers)
{
request.Headers.Add(header.Key, header.Value);
}
}
if (!String.IsNullOrEmpty(requestBody))
{
request.Headers.Add("Accept-Charset", "UTF-8");
byteArray = Encoding.UTF8.GetBytes(requestBody);
request.ContentLength = byteArray.Length;
}
// We now get our SharedAccessKey from the server
//request.Headers.Add("Authorization", AuthorizationHeader(method, now, request, ifMatch, md5));
if (!String.IsNullOrEmpty(requestBody))
{
request.GetRequestStream().Write(byteArray, 0, byteArray.Length);
}
return request;
}
/// <summary>
/// Creates the REST request direct UTF8.
/// </summary>
/// <param name="method">The method.</param>
/// <param name="resource">The resource.</param>
/// <param name="requestBodyUtf8">The request body UTF8.</param>
/// <param name="headers">The headers.</param>
/// <param name="ifMatch">If match.</param>
/// <param name="md5">The MD5.</param>
/// <returns></returns>
private HttpWebRequest CreateRESTRequestDirectUtf8(string method, string resource, byte[] requestBodyUtf8, SortedList<string, string> headers = null, string ifMatch = "", string md5 = "")
{
//byte[] byteArray = null;
DateTime now = DateTime.UtcNow;
Uri uri = new Uri(Endpoint + resource);
HttpWebRequest request = HttpWebRequest.Create(uri) as HttpWebRequest;
request.Method = method;
request.ContentLength = 0;
request.Headers.Add("x-ms-date", now.ToString("R", System.Globalization.CultureInfo.InvariantCulture));
request.Headers.Add("x-ms-version", "2009-09-19"); //2009-09-19, 2011-08-18
if (IsTableStorage)
{
request.ContentType = "application/atom+xml";
request.Headers.Add("DataServiceVersion", "1.0;NetFx");
request.Headers.Add("MaxDataServiceVersion", "1.0;NetFx");
}
// Additional headers can be passed in as a formal parameter:
if (headers != null)
{
foreach (KeyValuePair<string, string> header in headers)
{
request.Headers.Add(header.Key, header.Value);
}
}
if (requestBodyUtf8 != null)
{
request.Headers.Add("Accept-Charset", "UTF-8");
request.ContentLength = requestBodyUtf8.Length;
}
// We now get our SharedAccessKey from the server
//request.Headers.Add("Authorization", AuthorizationHeader(method, now, request, ifMatch, md5));
if (requestBodyUtf8 != null)
{
request.GetRequestStream().Write(requestBodyUtf8, 0, requestBodyUtf8.Length);
}
return request;
}
//
// Server side: The returned Uri here is the "Endpoint" used in the client code.
//
/// <summary>
/// Gets the blob-level shared access signature for the named blob
/// </summary>
/// <param name="blobName">The unique blob name Guid.</param>
/// <returns>The fully qualified Azure Shared Access Signature Query String to be used in azure upload connections</returns>
public Uri GetBlobUploadUrl(Guid blobName)
{
string containerName = BlobContainerName;
const string permissions = "rw";
string sharedAccessSignature = CreateSharedAccessSignature(containerName, blobName.ToString(), permissions);
string urlPath;
if (Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.IsEmulated)
{ // Emulation environment
urlPath = String.Format("{0}/{1}/{2}{3}", _blobEndpoint, containerName, blobName, sharedAccessSignature);
}
else
{ // Cloud
urlPath = String.Format("{0}{1}/{2}{3}", _blobEndpoint, containerName, blobName, sharedAccessSignature);
}
Uri uri = new Uri(urlPath);
return uri;
}
/// <summary>
/// Creates a blob-level shared access signature.
/// </summary>
/// <param name="containerName">The blob container name.</param>
/// <param name="blobName">The blob name, a unique ID which will be passed back to the client.</param>
/// <param name="permissions">String of access levels, "r" = read, "w" = write "rw" = both etc.</param>
/// <returns>The fully qualified Azure Shared Access Signature Query String</returns>
private string CreateSharedAccessSignature(string containerName, string blobName, string permissions)
{
// SAS without stored container policy
const string iso8061Format = "{0:yyyy-MM-ddTHH:mm:ssZ}";
DateTime startTime = DateTime.UtcNow.AddMinutes(-10d); //UtcNow;
DateTime expiryTime = startTime.AddMinutes(40d);
string start = string.Format(iso8061Format, startTime);
string expiry = string.Format(iso8061Format, expiryTime);
string stringToSign = string.Format("{0}\n{1}\n{2}\n/{3}/{4}/{5}\n", permissions, start, expiry, _accountName, containerName, blobName);
// SAS with stored container policy
//string stringToSign = String.Format("\n\n\n/{0}/{1}\n{2}", accountName, containerName, policyId);
string rawSignature = String.Empty;
Byte[] keyBytes = Convert.FromBase64String(_accountKey);
using (HMACSHA256 hmacSha256 = new HMACSHA256(keyBytes))
{
Byte[] utf8EncodedStringToSign = System.Text.Encoding.UTF8.GetBytes(stringToSign);
Byte[] signatureBytes = hmacSha256.ComputeHash(utf8EncodedStringToSign);
rawSignature = Convert.ToBase64String(signatureBytes);
}
string sharedAccessSignature = String.Format("?st={0}&se={1}&sr=b&sp={2}&sig={3}", Uri.EscapeDataString(start), Uri.EscapeDataString(expiry), permissions, Uri.EscapeDataString(rawSignature));
//
// End result will look like this in Fiddler if correct:
//PUT http://127.0.0.1:10000/devstoreaccount1/uploads/aecdfa39-7eaa-474a-9333-ecf43e6a0508?st=2012-08-17T16%3A11%3A53Z&se=2012-08-17T16%3A51%3A53Z&sr=b&sp=rw&sig=2%2Fs0R1L78S55uW5o2WontVvrZypckTriWoijnyrPbFc%3D&comp=block&blockid=AAAAAA== HTTP/1.1
//
return sharedAccessSignature;
}
感謝Guarav!太好了,這看起來和我的分段上傳代碼很相似,只不過你確認我們可以離開SharedKey請求頭。我試過了,但收到了一個「不好的請求」的消息,所以我一定有其他的錯誤。另外,顯然PUT塊的大小不一定是關鍵calc的一部分,因爲它在REST API中。我會告訴你。無論如何,我欠你一個(並且欠下一個堆棧溢出)。 R – GGleGrand 2012-08-16 20:13:50
您可以分享您的代碼以便查看嗎?我的猜測是,您要麼丟失了一個請求頭文件,要麼SAS代碼可能存在問題。 – 2012-08-17 05:12:59
我會在完成後發佈代碼 - 或者如果我無法正常工作:-)再次感謝! [R – GGleGrand 2012-08-17 12:19:21