Search code examples
c#.netazurerestazure-files

Write content to a file on azure storage with using PUT request in C#


I am trying to make a put request to Azure storage file, where I want to add some simple contents. I change the URL and add ?comp=range at the end of the url but I get 403 error in response. I have created a basic console application in .net.

My Header is :

 const string requestMethod = "PUT";
            string urlPath = strShareName + "/" + "rahila.csv?comp=range";//+ "?comp=range  HTTP/1.1";
            String canonicalizedResource = String.Format("/{0}/{1}/{2}", StorageAccountName, strShareName, strFileName);
            try
            {
                //GetWebRequest(requestMethod, urlPath, canonicalizedResource, "CreateFile");
                HttpWebRequest request = null;
                try
                {
                    const string type = "file";
                    string MethodType = "CreateFile";
                    const string msVersion = "2015-04-05";
                    String dateInRfc1123Format = DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture);
                    String canonicalizedHeaders = "";
                    string data = "rahila sted";
                    canonicalizedHeaders = String.Format("x-ms-date:{0}\nx-ms-version:{1}", dateInRfc1123Format, msVersion);
                    if (MethodType == "CreateFile")
                    {
                        canonicalizedHeaders = String.Format("x-ms-content-length:65536\nx-ms-date:{0}\nx-ms-type:file\nx-ms-version:{1}", dateInRfc1123Format, msVersion);
                    }

                    String stringToSign = "";


                    stringToSign = String.Format("{0}\n\n\n\n\n\n\n\n\n\n\n\n{1}\n{2}", requestMethod, canonicalizedHeaders, canonicalizedResource);

                    if (String.IsNullOrEmpty(stringToSign))
                    {
                        throw new ArgumentNullException("canonicalizedString");
                    }

                    String signature;

                    if (String.IsNullOrEmpty(stringToSign))
                    {
                        throw new ArgumentNullException("unsignedString");
                    }

                    if (Convert.FromBase64String(StorageKey) == null)
                    {
                        throw new ArgumentNullException("key");
                    }

                    Byte[] dataToHmac = System.Text.Encoding.UTF8.GetBytes(stringToSign);
                    using (HMACSHA256 hmacSha256 = new HMACSHA256(Convert.FromBase64String(StorageKey)))
                    {
                        signature = Convert.ToBase64String(hmacSha256.ComputeHash(dataToHmac));
                    }


                    String authorizationHeader = String.Format(CultureInfo.InvariantCulture, "{0} {1}:{2}",
                                                               StorageScheme,
                                                               StorageAccountName, signature);
Uri uri = new Uri(FileEndPoint + urlPath);
request = (HttpWebRequest)WebRequest.Create(uri);
if (requestMethod != "Get")
{
    request.ContentLength = data.Length;
}
// string data = "Hello testing";
//int a= ((data.Length) + 1);

request.Method = "PUT";//requestMethod;
request.Headers.Add("x-ms-write", "update");
request.Headers.Add("x-ms-date", dateInRfc1123Format);
request.Headers.Add("x-ms-version", msVersion);
request.Headers.Add("x-ms-range", "bytes=0-65535"); // + ((data.Length) - 1));
request.Headers.Add("Authorization", authorizationHeader);

the line where i get the exception is in the bold format.

HttpWebResponse response = null;
response = (HttpWebResponse)request.GetResponse();
string returnString = response.StatusCode.ToString();

Can anyone help me to resolve this issue or just guide me how to write content to a simple file on azure storage without using the azure client API.


Solution

  • update 12/19:

    When using Put Range to upload content to azure file, you can follow the following code(I assume you have already created a file on the azure file share, and it's content length is larger than the content being uploaded):

            static void UploadText()
            {
                string Account = "xxxx";
                string Key = "xxxx";
                string FileShare = "test1";
                string FileName = "11.txt";
                string apiversion = "2019-02-02";
    
                //the string to be uploaded to azure file, note that the length of the uploaded string should less than the azure file length
                string upload_text = "bbbbbbbbbbbbbbbbbbbbbbbb.";
                Console.WriteLine("the string length: " + upload_text.Length);
                DateTime dt = DateTime.UtcNow;
                string StringToSign = String.Format("PUT\n"
                    + "\n" // content encoding
                    + "\n" // content language
                    + upload_text.Length + "\n" // content length
                    + "\n" // content md5
                    + "\n" // content type
                    + "\n" // date
                    + "\n" // if modified since
                    + "\n" // if match
                    + "\n" // if none match
                    + "\n" // if unmodified since
                    + "\n"//+ "bytes=0-" + (upload_text.Length - 1) + "\n" // range
                    +"x-ms-date:" + dt.ToString("R") + "\nx-ms-range:bytes=0-"+(upload_text.Length-1) + "\nx-ms-version:" + apiversion + "\nx-ms-write:update\n" // headers
                    + "/{0}/{1}/{2}\ncomp:range", Account, FileShare, FileName);
    
                string auth = SignThis(StringToSign, Key, Account);
                string method = "PUT";
                string urlPath = string.Format("https://{0}.file.core.windows.net/{1}/{2}?comp=range", Account, FileShare,FileName);
                Uri uri = new Uri(urlPath);
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
                request.Method = method;
                request.ContentLength = upload_text.Length;
    
                request.Headers.Add("x-ms-range", "bytes=0-"+(upload_text.Length-1));
                request.Headers.Add("x-ms-write", "update");
                request.Headers.Add("x-ms-date", dt.ToString("R"));
                request.Headers.Add("x-ms-version", apiversion);
                request.Headers.Add("Authorization", auth);
                //request.Headers.Add("Content-Length", upload_text.Length.ToString());
    
                var bytes = System.Text.Encoding.ASCII.GetBytes(upload_text);
    
                using (var requestStream = request.GetRequestStream())
                {
                    requestStream.Write(bytes, 0, bytes.Length);
                }
    
    
                using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
                {
                    //read the content
                    Console.WriteLine("the response is:" + response.StatusCode);
                }
            }
    
    
    
            private static String SignThis(String StringToSign, string Key, string Account)
            {
                String signature = string.Empty;
                byte[] unicodeKey = Convert.FromBase64String(Key);
                using (HMACSHA256 hmacSha256 = new HMACSHA256(unicodeKey))
                {
                    Byte[] dataToHmac = System.Text.Encoding.UTF8.GetBytes(StringToSign);
                    signature = Convert.ToBase64String(hmacSha256.ComputeHash(dataToHmac));
                }
    
                String authorizationHeader = String.Format(
                      CultureInfo.InvariantCulture,
                      "{0} {1}:{2}",
                      "SharedKey",
                      Account,
                      signature);
    
                return authorizationHeader;
            }
    

    Then in the Main() method, you can call UploadText() method, it works at my side.


    old:

    guide me how to write content to a simple file on azure storage without using the azure client API.

    For this, you can directly use Azure File Storage SDK Microsoft.Azure.Storage.File, version 11.1.1. And we always recommend using SDK instead of using rest api, because the SDK is easy to use.

    Here is an example of using this SDK.

    First, create a console project of .NET framework in visual studio. Then install this nuget package Microsoft.Azure.Storage.File, version 11.1.1.

    The code:

    using Microsoft.Azure.Storage;
    using Microsoft.Azure.Storage.Auth;
    using Microsoft.Azure.Storage.File;
    using System;
    
    namespace AzureFileTest2
    {
        class Program
        {
            static void Main(string[] args)
            {
                string accountName = "xxx";
                string accountKey = "xxx";
                CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
    
                CloudFileClient cloudFileClient = storageAccount.CreateCloudFileClient();
    
                //make sure the file share named test1 exists.
                CloudFileShare fileShare = cloudFileClient.GetShareReference("test1");
                CloudFileDirectory fileDirectory = fileShare.GetRootDirectoryReference();
                CloudFile myfile = fileDirectory.GetFileReference("test123.txt");
    
                if (!myfile.Exists())
                {
                    //if the file does not exists, then create the file and set the file max size to 100kb.
                    myfile.Create(100 * 1024 * 1024);                
                }
    
                //upload text to the file
                //Besides using UploadText() method to directly upload text, you can also use UploadFromFile() / UploadFromByteArray() / UploadFromStream() methods as per your need.
                myfile.UploadText("hello, it is using azure storage SDK");
    
                Console.WriteLine("**completed**");
                Console.ReadLine();
            }
        }
    }