azurefilehttpazure-api-managementcloud-storage

Azure API Management rename file


Currently we have the front end sending files to an Azure storage account into specific blob containers. The front-end is manually getting SAS tokens put into the build via a person getting a SAS from the storage account and pasting it into the front-end code so it can read and write to the storage account.

We're wanting to have the front-end send a request to APIM with a file. We then want to hash that file, use that hash as the name and store it in azure blob storage. I'm new to Azure API Management, is this even possible? It seems like I can't get at the uploaded file.

In APIM policies I currently have the Authorization to the storage account working but I can't figure out how to get at the Request.Files like I normally would in an MVC app.

I've been looking all over https://learn.microsoft.com/ as well as https://techcommunity.microsoft.com/ and SO and I've even started looking on the second page of Google search results. I can't find anything that points to this being possible or not.

Here is my current policy. It works in the sense that the front-end can hit it and pass through a file and that file is saved. But we want to hash the file and use that hash as the name to avoid name collisions in the Azure storage account blob container

<policies>
    <inbound>
        <base />
        <set-variable name="UTCNow" value="@(DateTime.UtcNow.ToString("R"))" />
        <set-variable name="Verb" value="@(context.Request.Method)" />
        <set-variable name="documentstorage" value="{{documentstorage}}" />
        <set-variable name="documentstoragekey" value="{{documentstorageaccesskey}}" />
        <set-variable name="version" value="2019-12-12" />
        <set-variable name="bodySize" value="@(context.Request.Headers["Content-Length"][0])" />
        <set-variable name="contentType" value="@(context.Request.Headers["Content-Type"][0])" />
        <set-header name="x-ms-version" exists-action="override">
            <value>@((string)context.Variables["version"] )</value>
        </set-header>
        <set-header name="x-ms-blob-type" exists-action="override">
            <value>BlockBlob</value>
        </set-header>
        <set-header name="date" exists-action="override">
            <value>@((string)context.Variables["UTCNow"])</value>
        </set-header>
        <set-header name="Authorization" exists-action="override">
            <value>@{
                var account = (string)context.Variables["documentstorage"];
                var key = (string)context.Variables["documentstoragekey"];
                var verb = (string)context.Variables["Verb"];
                var container = context.Request.MatchedParameters["container"];
                var fileName = context.Request.MatchedParameters["fileName"];
                var dateNow = (string)context.Variables["UTCNow"];

                string contentType = (string)context.Variables["contentType"];//"application/pdf";
 
                var contentLength = (string)context.Variables["bodySize"];
                
                var stringToSign = string.Format("{0}\n\n\n{1}\n\n{2}\n{3}\n\n\n\n\n\nx-ms-blob-type:BlockBlob\nx-ms-version:{4}\n/{5}/{6}/{7}", 
                    verb, 
                    contentLength,
                    contentType,
                    (string)context.Variables["UTCNow"],
                    (string)context.Variables["version"],
                    account,
                    container,
                    fileName);



                string signature = "";
                var unicodeKey = Convert.FromBase64String(key);
                using (var hmacSha256 = new HMACSHA256(unicodeKey))
                {
                    var dataToHmac = Encoding.UTF8.GetBytes(stringToSign);
                    signature = Convert.ToBase64String(hmacSha256.ComputeHash(dataToHmac));
                }

                var authorizationHeader = string.Format(
                        "{0} {1}:{2}",
                        "SharedKey",
                        account,
                        signature);
                

                return authorizationHeader;
            }</value>
        </set-header>
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

Solution

  • I haven't tried this but it sounds like you can get the request body (that's where I assume your file is):

    var inBody = context.Request.Body.As<byte[]>(preserveContent: true);

    Based on this: https://learn.microsoft.com/en-us/azure/api-management/api-management-policy-expressions#ref-imessagebody and this https://learn.microsoft.com/en-us/azure/api-management/api-management-transformation-policies#SetBody

    However, if you just want to get a unique files names, why not simply generate a GUID? Or do you mean you want to make sure that every file only gets uploaded once? (Then hashing probably makes sense)