3

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.

Ivan Glasenberg
  • 29,865
  • 2
  • 44
  • 60
  • I'd look at the response, if I were you. There is a `HttpWebResponse` in the exception that's thrown, and you can retrieve the body from that to see what the error response is. Alternatively, use a tool like Telerik's Fiddler to view the request and error response. – ProgrammingLlama Dec 17 '19 at 07:02
  • The response i get is : at System.Net.HttpWebRequest.GetResponse() at NewFilecreateandupload.Program.UpdateContet(String strFileName, String Content) in C:\Users\abc\source\repos\NewFilecreateandupload\NewFilecreateandupload\Program.cs:line 406 and the error is The remote server returned an error: (403) Forbidden. – Rahila Syed Dec 17 '19 at 07:16
  • That's not the response body, that's the exception. See [here](https://stackoverflow.com/questions/7261986/how-to-get-error-information-when-httpwebrequest-getresponse-fails) – ProgrammingLlama Dec 17 '19 at 07:17
  • AuthenticationFailedServer failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature. RequestId:9917d3cd-f01a-006f-03aa-b400f3000000 Time:2019-12-17T07:22:38.8281666ZThe MAC signature found in the HTTP request '+in5TbFJHfF9BiLeYFFDGvIqPidiq/vrqB+PtDogboY=' is not the same as any computed signature. Server used following string to sign: – Rahila Syed Dec 17 '19 at 07:24
  • 'PUT x-ms-date:Tue, 17 Dec 2019 07:22:23 GMT x-ms-range:bytes=0-65535 x-ms-type:file x-ms-version:2015-04-05 x-ms-write:update /bc365storage/lables/rahila.csv comp:range'. – Rahila Syed Dec 17 '19 at 07:24
  • OK So you're possibly not building your authorization header correctly. – ProgrammingLlama Dec 17 '19 at 07:25
  • No idea, I'm afraid. I've always used the SDK. I'd recommend including your code to generate the header in your question though. – ProgrammingLlama Dec 17 '19 at 07:27
  • a) You should edit additional details into your question. b) You 100% have not included the code for _generating_ the header in your question. You've simply included the variable `authorizationHeader`, but we have no idea how that variable came into being. It could simply be `string authorizationHeader = "hello";`, in which case it definitely wouldn't work as an authorization header. Signatures usually involve hashing, etc. and there's none of that in your question. – ProgrammingLlama Dec 17 '19 at 07:30
  • I have updated my question – Rahila Syed Dec 17 '19 at 07:35

1 Answers1

1

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();
        }
    }
}
Ivan Glasenberg
  • 29,865
  • 2
  • 44
  • 60
  • i am restricted not to to use the Azure File Storage SDK, we have to make it work using rest api, – Rahila Syed Dec 19 '19 at 07:03
  • @RahilaSyed, ok, then what's the meaning of "or just guide me how to write content to a simple file on azure storage without using the azure client API." in your post? I will try to use rest api for this, and update you later. – Ivan Glasenberg Dec 19 '19 at 07:06
  • @RahilaSyed, please see the updated section. And let me know if you still have more issues about that. – Ivan Glasenberg Dec 19 '19 at 08:39