我们目前使用REST API(基于微软样本)从.NET客户端配置文件机器上逐块上传blob。 REST API示例直接使用Azure存储帐户名和访问密钥在请求标头中构造SharedKey条目。对于生产代码,我们需要计算服务器上的SharedKey,并将其交付给客户端以在会话期间使用。
为blob创建SharedKey的示例为我提供了一个包含访问参数的Url plus查询字符串。
我的问题:如何将此Url /查询字符串键格式与Azure REST API所需的SharedKey标头条目结合使用?
任何指针或提示都非常感谢! [R
答案 0 :(得分:1)
我认为您需要生成共享访问签名(SAS)URL。我对么?生成SAS URL时,权限将在URI本身中进行编码,这样您就不必再使用授权密钥了。
为了生成SAS,您可能会发现这两个链接很有用:
http://msdn.microsoft.com/en-us/library/windowsazure/hh508996
http://msdn.microsoft.com/en-us/library/windowsazure/ee395415
答案 1 :(得分:1)
你走了。显然可以对这段代码做很多改进:)试一试。如果它适合你,请告诉我。我可以使用以下代码在开发存储中上传blob:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Web;
using System.Net;
using System.Collections.Specialized;
using System.Globalization;
namespace UploadBlobUsingSASUrl
{
class Program
{
//This is part of SAS signature (query string). We will construct the URI later using this.
private static string sasSignature = "sr=c&st=2012-08-16T14%3A38%3A48Z&se=2012-08-16T15%3A38%3A48Z&sp=w&sig=aNTLYQtwA1UmjG7j8Lg44t8YThL16FkNYBi54kl4ZKo%3D";
//Blob storage endpoint
private static string blobStorageEndpoint = "http://127.0.0.1:10000/devstoreaccount1";
//Blob container name
private static string blobContainerName = "[blob container name. SAS URI with Write permission must be created on this blob container]";
//File to upload
private static string fileToUpload = @"[Full path of the file you wish to upload]";
//This is the default block size (This application always assumes that a file will be split in blocks and then uploaded).
private static int blockSize = 256 * 1024;//256 KB
//Storage service version (Unless you're using latest SAS related changes in cloud storage, use this version). For development storage always use this version.
private static string x_ms_version = "2011-08-18";
//Template for put block list
private static string blockListTemplate = @"{0}";
// Template for block id (to be included in put block list template)
private static string blockIdTemplate = "{0}";
//We'll keep a list of block ids.
private static List blockIds = new List();
static void Main(string[] args)
{
FileInfo file = new FileInfo(fileToUpload);
long totalFileSize = file.Length;//Get the file size
long bytesFrom = 0;
long bytesRemaining = totalFileSize;
string blobName = file.Name;
//This is the base URI which will be used for put blocks and put block list operations.
//It is essentially would be something like "http://127.0.0.1:10000/devstoreaccount1/myblobcontainer/myblobname?sassignature"
string baseUri = string.Format("{0}/{1}/{2}?{3}", blobStorageEndpoint, blobContainerName, blobName, sasSignature);
int counter = 0;
//In this loop, we'll read file in chunks and try and upload one chunk at a time.
while (true)
{
int bytesToRead = blockSize;
if (bytesRemaining < blockSize)
{
bytesToRead = (int)bytesRemaining;
}
//Read the file in chunks
byte[] fileContents = ReadFile(fileToUpload, bytesFrom, bytesToRead);
bytesRemaining -= fileContents.Length;
bytesFrom += fileContents.Length;
//Create block id
string blockId = string.Format("Block-{0:D5}", counter);
//Append that to the block id list.
blockIds.Add(blockId);
//Now let's upload the block.
var isBlockUploaded = UploadBlock(baseUri, fileContents, blockId);
Console.WriteLine("Block Id: " + blockId + " Block Size: " + fileContents.Length + " Uploaded: " + isBlockUploaded);
counter++;
if (bytesRemaining <= 0)
{
break;
}
}
//All blocks uploaded, now let's commit the block list
var isBlockListCommitted = CommitBlockList(baseUri, blockIds);
Console.WriteLine("Is Block List Committed: " + isBlockListCommitted);
Console.WriteLine("Press any key to terminate the program ....");
Console.ReadLine();
}
/// <summary>
/// This function reads a chunk of the file and returns that as byte array.
/// </summary>
/// <param name="fileName"></param>
/// <param name="bytesFrom"></param>
/// <param name="bytesToRead"></param>
/// <returns></returns>
private static byte[] ReadFile(string fileName, long bytesFrom, int bytesToRead)
{
using (FileStream fs = new FileStream(fileName, FileMode.Open))
{
byte[] byteArray = new byte[bytesToRead];
fs.Seek(bytesFrom, SeekOrigin.Begin);
fs.Read(byteArray, 0, bytesToRead);
return byteArray;
}
}
/// <summary>
/// This function uploads a block.
/// </summary>
/// <param name="baseUri"></param>
/// <param name="blockContents"></param>
/// <param name="blockId"></param>
/// <returns></returns>
private static bool UploadBlock(string baseUri, byte[] blockContents, string blockId)
{
bool isBlockUploaded = false;
//Create request URI -
string uploadBlockUri = string.Format("{0}&comp=block&blockId={1}", baseUri, Convert.ToBase64String(Encoding.UTF8.GetBytes(blockId)));
// Create request object
var request = (HttpWebRequest) HttpWebRequest.Create(uploadBlockUri);
NameValueCollection requestHeaders = new NameValueCollection();
var requestDate = DateTime.UtcNow;
//Add request headers. Please note that since we're using SAS URI, we don't really need "Authorization" header.
requestHeaders.Add("x-ms-date", string.Format(CultureInfo.InvariantCulture, "{0:R}", requestDate));
requestHeaders.Add("x-ms-version", x_ms_version);
request.Headers.Add(requestHeaders);
//Set content length header.
request.ContentLength = blockContents.Length;
//Set request HTTP method.
request.Method = "PUT";
// Send the request
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(blockContents, 0, blockContents.Length);
}
// Get the response
using (var response = (HttpWebResponse)request.GetResponse())
{
isBlockUploaded = response.StatusCode.Equals(HttpStatusCode.Created);
}
return isBlockUploaded;
}
/// <summary>
/// This function commits the block list.
/// </summary>
/// <param name="baseUri"></param>
/// <param name="blockIds"></param>
/// <returns></returns>
private static bool CommitBlockList(string baseUri, List<string> blockIds)
{
bool isBlockListCommitted = false;
//Create the request payload
StringBuilder blockIdsPayload = new StringBuilder();
foreach (var blockId in blockIds)
{
blockIdsPayload.AppendFormat(blockIdTemplate, Convert.ToBase64String(Encoding.UTF8.GetBytes(blockId)));
}
string putBlockListPayload = string.Format(blockListTemplate, blockIdsPayload.ToString());
// Create request URI
string putBlockListUrl = string.Format("{0}&comp=blocklist", baseUri);
// Create request object.
var request = (HttpWebRequest)HttpWebRequest.Create(putBlockListUrl);
NameValueCollection requestHeaders = new NameValueCollection();
//Add request headers. Please note that since we're using SAS URI, we don't really need "Authorization" header.
var requestDate = DateTime.UtcNow;
requestHeaders.Add("x-ms-date", string.Format(CultureInfo.InvariantCulture, "{0:R}", requestDate));
requestHeaders.Add("x-ms-version", x_ms_version);
byte[] requestPayload = Encoding.UTF8.GetBytes(putBlockListPayload);
//Set content length header.
request.ContentLength = requestPayload.Length;
request.Headers.Add(requestHeaders);
//Set request HTTP method.
request.Method = "PUT";
// Send the request
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(requestPayload, 0, requestPayload.Length);
}
// Get the response
using (var response = (HttpWebResponse)request.GetResponse())
{
isBlockListCommitted = response.StatusCode.Equals(HttpStatusCode.Created);
}
return isBlockListCommitted;
}
}
}
答案 2 :(得分:0)
这是我在仿真环境中成功测试的结果代码(减去一些要做的清理)。首先,客户端代码是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;
}