Search code examples
azureazure-blob-storageazure-storage-emulator

How to use Azure storage emulator blob endpoint to get a blob?


The following code, when pointed to a real Azure storage account will successfully return the blob content:

var path = $"{container}/{blob}";
var rfcDate = DateTime.UtcNow.ToString("R");
var headers =  "GET\n\n\n\n\n\n\n\n\n\n\n\n" +
               "x-ms-blob-type:Block\n" +
              $"x-ms-date:{rfcDate}\n" + 
              $"x-ms-version:{ServiceVersion}\n" +
              $"/{AccountName}/{path}";
var uri = new Uri(BlobEndpoint + path);
var request = new HttpRequestMessage(HttpMethod.Get, uri);
request.Headers.Add("x-ms-blob-type", "Block");
request.Headers.Add("x-ms-date", rfcDate);
request.Headers.Add("x-ms-version", ServiceVersion);

string signature = "";
using (var sha = new HMACSHA256(System.Convert.FromBase64String(AccountKey)))
{
  var data = Encoding.UTF8.GetBytes(headers);
  signature = System.Convert.ToBase64String(sha.ComputeHash(data));
}

var authHeader = $"SharedKey {AccountName}:{signature}";
request.Headers.Add("Authorization", authHeader);

using (var client = new HttpClient())
{
  var response = await client.SendAsync(request);
  return await response.Content.ReadAsStringAsync();
}

However, if I configure it to use the Azure emulator where:

  • AccountName = devstoreaccount1
  • AccountKey = Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==
  • BlobEndpoint = http://127.0.0.1:10000/
  • ServiceVersion = 2009-09-19

I always get 404. I'm using Azure Storage Emulator v4.6. Is the code or config incorrect or is this not supported with the emulator?


Solution

  • There are two issues with your code:

    1. Blob Service in Storage Emulator listens at http://127.0.0.1:1000 however the base URI is http://127.0.0.1:1000/devstoreaccount1.
    2. In computation of Signature String (header variable in your code), account name must appear twice. This is because the account name is part of your resource's URI path (URL for the blob would be http://127.0.0.1:1000/devstoreaccount1/container-name/blob-name).

    Based on these, please try the following code:

        static async Task<string> ReadBlobFromDevStorage()
        {
            var container = "temp";
            var blob = "test.txt";
            var ServiceVersion = "2009-09-19";
            var AccountName = "devstoreaccount1";
            var BlobEndpoint = "http://127.0.0.1:10000/devstoreaccount1";
            var path = $"{container}/{blob}";
            var AccountKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
            var rfcDate = DateTime.UtcNow.ToString("R");
            var headers = "GET\n\n\n\n\n\n\n\n\n\n\n\n" +
                           "x-ms-blob-type:Block\n" +
                          $"x-ms-date:{rfcDate}\n" +
                          $"x-ms-version:{ServiceVersion}\n" +
                          $"/{AccountName}/{AccountName}/{path}";
            var uri = new Uri(BlobEndpoint + "/" + path);
            var request = new HttpRequestMessage(HttpMethod.Get, uri);
            request.Headers.Add("x-ms-blob-type", "Block");
            request.Headers.Add("x-ms-date", rfcDate);
            request.Headers.Add("x-ms-version", ServiceVersion);
    
            string signature = "";
            using (var sha = new HMACSHA256(System.Convert.FromBase64String(AccountKey)))
            {
                var data = Encoding.UTF8.GetBytes(headers);
                signature = System.Convert.ToBase64String(sha.ComputeHash(data));
            }
    
            var authHeader = $"SharedKey {AccountName}:{signature}";
            request.Headers.Add("Authorization", authHeader);
    
            using (var client = new HttpClient())
            {
                var response = await client.SendAsync(request);
                var content = await response.Content.ReadAsStringAsync();
                Console.WriteLine(content);
                return content;
            }
        }