Following my recent post Call Azure REST API from SQL Server I've received many requests about how to call the new API Copy Blob. Its description is here: http://msdn.microsoft.com/en-us/library/windowsazure/dd894037.aspx. As before, let me show you what we want to achieve:
We want to have a simple stored procedure to call with a storage account, a source and a destination blob (we need the shared key too).
EXEC Azure.CopyBlob '<storage>','<source container>','<source blob>','<destination container>','<destination blob>','<sharedkey>';
What we should do is to call the correct REST entity we want to create using the PUT verb. We must specify as header the source blob: x-ms-copy-source:name (don't forget to include it in the signature otherwise you'll get a very unpolite 403 forbidden!). Here is the main procedure code:
privatestatic Dictionary<string,string> _CopyPageBlob(string storageAccount,string srcCcontainer,string srcBlobName,string destContainer,string destBlobName,string sharedKey){string strCopySource =string.Format("https://{0:S}.blob.core.windows.net/{1:S}/{2:S}", storageAccount, srcCcontainer, srcBlobName);string strCopyDestination =string.Format("https://{0:S}.blob.core.windows.net/{1:S}/{2:S}", storageAccount, destContainer, destBlobName);string CanonicalizedResource ="/"; CanonicalizedResource += storageAccount; CanonicalizedResource +="/"+ destContainer +"/"+ destBlobName; System.Net.HttpWebRequest Request =(System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(strCopyDestination); Request.Method ="PUT"; Request.ContentLength =0;#region Add HTTP headersstring strDate = DateTime.UtcNow.ToString("R"); Request.Headers.Add("x-ms-copy-source", strCopySource); Request.Headers.Add("x-ms-date", strDate); Request.Headers.Add("x-ms-version","2012-02-12");#endregionstring CanonicalizedHeader ="x-ms-copy-source:"+ strCopySource +"\n"; CanonicalizedHeader +="x-ms-date:"+ strDate +"\n"; CanonicalizedHeader +="x-ms-version:2012-02-12\n";string MessageSignature = Request.Method +"\n"; MessageSignature +="\n"; MessageSignature +="\n"; MessageSignature += Request.ContentLength +"\n"; MessageSignature +="\n"; MessageSignature +="\n"; MessageSignature +="\n"; MessageSignature +="\n"; MessageSignature +="\n"; MessageSignature +="\n"; MessageSignature +="\n"; MessageSignature +="\n"; MessageSignature += CanonicalizedHeader; MessageSignature += CanonicalizedResource; byte[] SignatureBytes = System.Text.Encoding.UTF8.GetBytes(MessageSignature); System.Security.Cryptography.HMACSHA256 SHA256 =new System.Security.Cryptography.HMACSHA256(Convert.FromBase64String(sharedKey));String AuthorizationHeader ="SharedKey "+ storageAccount +":"+ Convert.ToBase64String(SHA256.ComputeHash(SignatureBytes)); Request.Headers.Add("Authorization", AuthorizationHeader);using(System.Net.HttpWebResponse response =(System.Net.HttpWebResponse)Request.GetResponse()){if(response.StatusCode == System.Net.HttpStatusCode.Accepted){ Dictionary<string,string> dRes =new Dictionary<string,string>();for(int i =0; i < response.Headers.Count; i++) dRes.Add(response.Headers.GetKey(i), response.Headers.Get(i));return dRes;}elsethrownew Exception(response.ToString());}}
The wrapper SQL method is very simple then:
[SqlProcedure]publicstaticvoid CopyPageBlob(SqlString storageAccount, SqlString sourceCcontainer, SqlString sourceBlobName, SqlString destinationContainer, SqlString destinationBlobName, SqlString sharedKey){ Dictionary<string,string> dRes = _CopyPageBlob( storageAccount.ToString(), sourceCcontainer.ToString(), sourceBlobName.ToString(), destinationContainer.ToString(), destinationBlobName.ToString(), sharedKey.ToString()); PushRecordset(dRes);}
That alone should enable you to use this fantastic Azure feature. One point is to note, though: you might get the result before the copy is finished. The x-ms-copy-status header will tell you that (either success of pending); that's why we push it back to the caller in a recordset.
Happy coding,