問題分析
首先關(guān)于 Azure 存儲(chǔ)中 MD5 的描述,我們已經(jīng)有相關(guān)的介紹文檔,如果對于存儲(chǔ)中 MD5 的描述不熟悉,可以先參考 Azure Blob 存儲(chǔ)基于 MD5 的完整性檢查的內(nèi)容。
如果直接將文件上傳到 Blob 中可以在上傳的方法中配置 BlobRequestOptions 類,將該類的 StoreBlobContentMD5 參數(shù)設(shè)置為 true,即可在上傳時(shí)自動(dòng)計(jì)算 MD5 值并將此值寫入到請求頭部(Content-MD5)中(可以參考 BlobRequestOptions.StoreBlobContentMD5 Property 此文檔的描述)。 但是如果使用斷點(diǎn)續(xù)傳的方法,是將文件分為多個(gè)塊上傳,之后通過 PubBlockList 請求完成組合,那么想要上傳 MD5 值,需要在 PubBlockList 請求的頭部添加 x-ms-blob-content-md5 參數(shù),但是在 sdk 相關(guān)的方法中,BlobRequestOptions 中并沒有關(guān)于該參數(shù)的屬性,所以如果使用斷點(diǎn)續(xù)傳,采用 sdk 的 PubBlockList() 方法無法將 MD5 值上傳上去,本篇文檔即要解決如何在斷點(diǎn)續(xù)傳時(shí)上傳 MD5 值的問題。
解決方案
可以通過使用 REST API 的方式來解決此問題:
1.首先我們需要計(jì)算出文件的 MD5 值:
string contentHash = md5()(File.ReadAllBytes(sourcePath));
2.將文件分塊上傳:
public async Task PutBlobAsync(String containerName, String blobName, byte[] blobContent, String blobid, bool error = false)
{
String requestMethod = "PUT";
String urlPath = String.Format("{0}/{1}", containerName, blobName) + "?comp=block&blockid=" + blobid;
String storageServiceVersion = "2015-02-21";
String dateInRfc1123Format = DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture);
Int32 blobLength = blobContent.Length;
//headers
String canonicalizedHeaders = String.Format(
"\nx-ms-date:{0}\nx-ms-version:{1}",
dateInRfc1123Format,
storageServiceVersion);
//resources
String canonicalizedResource = String.Format("/{0}/{1}", AzureConstants.Account, String.Format("{0}/{1}", containerName, blobName) + "\nblockid:" + blobid + "\ncomp:block");
String stringToSign = String.Format(
"{0}\n\n\n{1}\n\n\n\n\n\n\n\n{2}\n{3}",
requestMethod,
blobLength,
canonicalizedHeaders,
canonicalizedResource);
string authorizationHeader = CreateAuthorizationHeader(stringToSign);
//上傳url
Uri uri = new Uri(BlobEndPoint + urlPath);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = requestMethod;
request.Headers["x-ms-date"] = dateInRfc1123Format;
request.Headers["x-ms-version"] = storageServiceVersion;
request.Headers["Authorization"] = authorizationHeader;
request.ContentLength = blobLength;
try {
using (Stream requestStream = await request.GetRequestStreamAsync()) {
requestStream.Write(blobContent, 0, blobLength);
}
using (HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync()) {
String ETag = response.Headers["ETag"];
System.Console.WriteLine(ETag);
}
error = false;
}
catch (WebException ex) {
System.Console.WriteLine("An error occured. Status code:" + ((HttpWebResponse)ex.Response).StatusCode);
System.Console.WriteLine("Error information:");
error = true;
using (Stream stream = ex.Response.GetResponseStream()) {
using (StreamReader sr = new StreamReader(stream)) {
var s = sr.ReadToEnd();
System.Console.WriteLine(s);
}
}
}
}
3.在 PutBlobListAsync() 方法中將 MD5 值和 x-ms-blob-content-md5 寫入到請求頭中:
public async Task PutBlobListAsync(String containerName, String blobName, List<string> blobIdList, string md5, bool error = false)
{
String requestMethod = "PUT";
String urlPath = String.Format("{0}/{1}", containerName, blobName) + "?comp=blocklist";
String storageServiceVersion = "2015-02-21";
String dateInRfc1123Format = DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture);
String canonicalizedHeaders = String.Format(
"\nx-ms-blob-content-md5:{0}\nx-ms-date:{1}\nx-ms-version:{2}",
md5,
dateInRfc1123Format,
storageServiceVersion);
StringBuilder stringbuilder = new StringBuilder();
stringbuilder.Append("<BlockList>");
foreach (string item in blobIdList) {
stringbuilder.Append(" <Latest>" + item + "</Latest>");
}
stringbuilder.Append("</BlockList>");
byte[] data = Encoding.UTF8.GetBytes(stringbuilder.ToString());
Int32 blobLength = data.Length;
String canonicalizedResource = String.Format("/{0}/{1}", AzureConstants.Account, String.Format("{0}/{1}", containerName, blobName) + "\ncomp:blocklist");
String stringToSign = String.Format(
"{0}\n\n\n{1}\n\n\n\n\n\n\n\n{2}\n{3}",
requestMethod,
blobLength,
canonicalizedHeaders,
canonicalizedResource);
String authorizationHeader = CreateAuthorizationHeader(stringToSign);
Uri uri = new Uri(BlobEndPoint + urlPath);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = requestMethod;
request.Headers["x-ms-blob-content-md5"] = md5;
request.Headers["x-ms-date"] = dateInRfc1123Format;
request.Headers["x-ms-version"] = storageServiceVersion;
request.Headers["Authorization"] = authorizationHeader;
request.ContentLength = blobLength;
try {
using (Stream requestStream = await request.GetRequestStreamAsync()) {
requestStream.Write(data, 0, blobLength);
}
using (HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync()) {
String ETag = response.Headers["ETag"];
System.Console.WriteLine(ETag);
}
error = false;
}
catch (WebException ex) {
System.Console.WriteLine("An error occured. Status code:" + ((HttpWebResponse)ex.Response).StatusCode);
System.Console.WriteLine("Error information:");
error = true;
using (Stream stream = ex.Response.GetResponseStream()) {
using (StreamReader sr = new StreamReader(stream)) {
var s = sr.ReadToEnd();
System.Console.WriteLine(s);
}
}
}
}
完整示例請參考示例代碼。