We currently use the REST API (based on microsoft samples) to upload blobs blockwise from a .NET client profile machine. The REST API examples use the Azure storage account name and access key directly to construct the SharedKey entry in the request header. For production code, we’ll need to calculate the SharedKey on our server, and deliver it for the client to use during the session.
Examples of SharedKey creation for blobs provide me with a Url plus query string that contains the access parameters.
My question: How do I get this Url/query string key format work in conjunction with the SharedKey header entry required by the Azure REST API?
Any pointers or tips greatly appreciated!
R
Here you go. Obviously a lot of improvement can be made to this code 🙂 Give it a try. Do let me know if it works for you. I was able to upload a blob in development storage using the code below:
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;
}
}
}