2

現在、REST API (Microsoft のサンプルに基づく) を使用して、.NET クライアント プロファイル マシンから BLOB をブロックごとにアップロードしています。REST API の例では、Azure ストレージ アカウント名とアクセス キーを直接使用して、要求ヘッダーの SharedKey エントリを作成します。本番コードの場合、サーバーで SharedKey を計算し、それをクライアントがセッション中に使用できるように配信する必要があります。

BLOB の SharedKey 作成の例では、Url と、アクセス パラメーターを含むクエリ文字列が提供されます。

私の質問: Azure REST API で必要な SharedKey ヘッダー エントリと組み合わせて、この Url/クエリ文字列キー形式を機能させるにはどうすればよいですか?

ポインタやヒントは大歓迎です!R

4

3 に答える 3

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; } }

}

于 2012-08-16T15:17:25.387 に答える
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;
    }
于 2012-08-17T18:52:40.537 に答える
0

Shared Access Signature (SAS) URL を生成する必要があると思います。私は正しいですか?SAS URL を生成すると、URI 自体にアクセス許可がエンコードされるため、認証キーを使用する必要がなくなります。

SAS を生成するには、次の 2 つのリンクが役立つ場合があります。

http://msdn.microsoft.com/en-us/library/windowsazure/hh508996

http://msdn.microsoft.com/en-us/library/windowsazure/ee395415

于 2012-08-16T08:45:26.187 に答える