none
How to Upload files to azure data lake storage generation 2 using shared key (Rest Api) in c#?

    Question

  • I want to know a way to upload files on azure data lake storage generation 2 using shared key (Rest API) in c#. 

    I am facing issue in uploading files. It gives below error.


    Below is my code.File is getting created properly But it fails when i try to append data in created file.Please assist me if i am   doing anything wrong.

       

    private static AuthenticationHeaderValue GetAuthorizationHeaderUpload(
             string storageAccountName, string storageAccountKey, DateTime now,
             HttpRequestMessage httpRequestMessage, string ifMatch = "", string md5 = "")
            {
                AuthenticationHeaderValue authHV = null;
                try
                {
                    // This is the raw representation of the message signature.
                    HttpMethod method = httpRequestMessage.Method;
                    String MessageSignature = String.Format("{0}\n\n\n{1}\n{5}\n\n\n\n{2}\n\n\n\n{3}{4}\n{5}\n{6}",
                              method.ToString(),
                              (method == HttpMethod.Get || method == HttpMethod.Head || method == HttpMethod.Put || method == HttpMethod.Delete) ? String.Empty
                                : httpRequestMessage.Content.Headers.ContentLength.ToString(),
                              ifMatch,
                              GetCanonicalizedHeaders(httpRequestMessage),
                              GetCanonicalizedResource(httpRequestMessage.RequestUri, storageAccountName),
                              md5);

                    // Now turn it into a byte array.
                    byte[] SignatureBytes = Encoding.UTF8.GetBytes(MessageSignature);

                    // Create the HMACSHA256 version of the storage key.
                    HMACSHA256 SHA256 = new HMACSHA256(Convert.FromBase64String(storageAccountKey));

                    // Compute the hash of the SignatureBytes and convert it to a base64 string.
                    string signature = Convert.ToBase64String(SHA256.ComputeHash(SignatureBytes));

                    // This is the actual header that will be added to the list of request headers.
                    // You can stop the code here and look at the value of 'authHV' before it is returned.
                    authHV = new AuthenticationHeaderValue("SharedKey",
                       storageAccountName + ":" + Convert.ToBase64String(SHA256.ComputeHash(SignatureBytes)));
                }
                catch (Exception ex)
                {
                    authHV = null;
                }
                return authHV;
            }

      /// <summary>
            /// Put the headers that start with x-ms in a list and sort them.
            /// Then format them into a string of [key:value\n] values concatenated into one string.
            /// (Canonicalized Headers = headers where the format is standardized).
            /// </summary>
            /// <param name="httpRequestMessage">The request that will be made to the storage service.</param>
            /// <returns>Error message; blank if okay.</returns>
            private static string GetCanonicalizedHeaders(HttpRequestMessage httpRequestMessage)
            {
                var headers = from kvp in httpRequestMessage.Headers
                              where kvp.Key.StartsWith("x-ms-", StringComparison.OrdinalIgnoreCase)
                              orderby kvp.Key
                              select new { Key = kvp.Key.ToLowerInvariant(), kvp.Value };

                StringBuilder sb = new StringBuilder();

                // Create the string in the right format; this is what makes the headers "canonicalized" --
                //   it means put in a standard format. http://en.wikipedia.org/wiki/Canonicalization
                foreach (var kvp in headers)
                {
                    StringBuilder headerBuilder = new StringBuilder(kvp.Key);
                    char separator = ':';

                    // Get the value for each header, strip out \r\n if found, then append it with the key.
                    foreach (string headerValues in kvp.Value)
                    {
                        string trimmedValue = headerValues.TrimStart().Replace("\r\n", String.Empty);
                        headerBuilder.Append(separator).Append(trimmedValue);

                        // Set this to a comma; this will only be used 
                        //   if there are multiple values for one of the headers.
                        separator = ',';
                    }
                    sb.Append(headerBuilder.ToString()).Append("\n");
                }
                return sb.ToString();
            }

     /// <summary>
            /// This part of the signature string represents the storage account 
            ///   targeted by the request. Will also include any additional query parameters/values.
            /// For ListContainers, this will return something like this:
            ///   /storageaccountname/\ncomp:list
            /// </summary>
            /// <param name="address">The URI of the storage service.</param>
            /// <param name="accountName">The storage account name.</param>
            /// <returns>String representing the canonicalized resource.</returns>
            private static string GetCanonicalizedResource(Uri address, string storageAccountName)
            {
                // The absolute path is "/" because for we're getting a list of containers.
                StringBuilder sb = new StringBuilder("/").Append(storageAccountName).Append(address.AbsolutePath);

                // Address.Query is the resource, such as "?comp=list".
                // This ends up with a NameValueCollection with 1 entry having key=comp, value=list.
                // It will have more entries if you have more query parameters.
                NameValueCollection values = ParseQueryString(address.Query);

                foreach (var item in values.AllKeys.OrderBy(k => k))
                {
                    values[item] = WebUtility.UrlDecode(values[item]);
                    if (!string.IsNullOrEmpty(item))
                        sb.Append('\n').Append(item).Append(':').Append(values[item]);
                }

                return sb.ToString();

            }


    Tuesday, April 16, 2019 5:13 PM

All replies

  • Hi,

    Please can anyone help me in this.

    Wednesday, April 17, 2019 12:08 PM
  • When you created the file , did you used the same GetAuthorizaHeader method to get the details and call the API ? If the answer is "yes" , then I think that the shared key is missing is required permission .Please check the permission on the portal .

    Thanks Himanshu

    Wednesday, April 17, 2019 6:56 PM
    Moderator
  • Hi,

    Yes i am using same GetAuthorizaHeader method, can you please tell me from where i can give permission to shared key.

    If you can give me an example of upload file in rest c#, it will be really helpful to me.

    Thursday, April 18, 2019 2:07 PM
  • Please navigate to the azure portal Home ->your Storage account -> Shared access signature and you should br able to see the permissions .

    Thanks Himanshu

    Thursday, April 18, 2019 6:44 PM
    Moderator