Updating images in File service/Azure using REST API-invalid header value 'Content-Length'

1.4k views Asked by At

I'm trying to upload the images to FILE SERVICE in Azure using REST API documentation specified here: https://learn.microsoft.com/en-us/rest/api/storageservices/put-range

visual force page:

<apex:page controller="azure_cls_cpy">
<apex:form >
    <apex:inputFile value="{!img}" filename="{!fileName}" fileSize="{!fileSize}" ></apex:inputFile>
    <apex:commandButton value="Click" action="{!createImage}"/><br/>
    <apex:messages />
</apex:form>
</apex:page>

Here is my code, which is not working:

public void createImage(){
    string storageKey = 'xxxxStorageKeyxxxx';
    string storageName = '<storageName>';
    Datetime dt = Datetime.now();
    string formattedDate = dt.formatGMT('EEE, dd MMM yyyy HH:mm:ss')+ ' GMT';

    string CanonicalizedHeaders = 'x-ms-date:'+formattedDate+'\nx-ms-range:bytes=0-'+string.valueOf(fileSize)+'\nx-ms-version:2016-05-31\nx-ms-write:update';

    string CanonicalizedResource = '/' + storageName + '/<shareName>/<DirectoryName>/'+fileName+'\ncomp:range\ntimeout:20';
        string StringToSign = 'PUT\n\n\nstring.valueOf(fileSize)\n\n\n\n\n\n\n\n\n' + CanonicalizedHeaders+'\n'+CanonicalizedResource;
        system.debug('StringToSign--'+StringToSign);

    Blob temp = EncodingUtil.base64Decode(storageKey);
    Blob hmac = Crypto.generateMac('HmacSHA256',Blob.valueOf(StringToSign),temp ); //StringToSign
    system.debug('oo-'+EncodingUtil.base64Encode(hmac));
    HttpRequest req = new HttpRequest();
    req.setMethod('PUT');
    req.setHeader('x-ms-version','2016-05-31' );
    req.setHeader('x-ms-date', formattedDate);
    req.setHeader('Content-Length',string.valueOf(fileSize);
    req.setHeader('x-ms-range','bytes=0-'+string.valueOf(fileSize));        
    req.setHeader('x-ms-write','update');
    string signature = EncodingUtil.base64Encode(hmac);
    string authHeader =  'SharedKey biznussoftfiles'+':'+signature;

    req.setHeader('Authorization',authHeader);
    req.setEndpoint('https://<storageName>.file.core.windows.net/<shareName>/<directoryName>/'+fileName+'?comp=range&timeout=20');
    req.setBodyAsBlob(img);              
    Http http = new Http();
    HTTPResponse res;
    res = http.send(req);                
}

In my code, fileName,fileSize,img are the properties of the image i'm trying to upload(which are dynamic).

Here,I'm facing issue with Content-Length. Below is the error response:

<?xml version="1.0" encoding="utf-8"?>
<Error><Code>InvalidHeaderValue</Code>
<Message>The value for one of the HTTP headers is not in the correct format.
RequestId:0996d8a7-001a-0060-7376-2ce658000000
Time:2017-09-13T09:53:06.4734435Z</Message>
<HeaderName>Content-Length</HeaderName>
<HeaderValue>197844</HeaderValue>
 </Error>

Updated Note: Reverted the range back to 0-filesize() to avoid Invalid Range error.

2

There are 2 answers

2
Gaurav Mantri On BEST ANSWER

I should have seen the issue sooner :). Essentially the issue was with your x-ms-range header. Because it starts from 0, the ending value should be the length of the image - 1. Once you do that, the code should work just fine.

Please see the code below:

public class azure_cls_cpy {
    public string fileName {get; set;}
    public integer fileSize {get; set;}
    public blob img {get; set;}

    public void createImage(){
        string storageKey = 'account-key';
        string storageName = 'account-name';
        string shareName = 'share-name';
        Datetime dt = Datetime.now();
        string formattedDate = dt.formatGMT('EEE, dd MMM yyyy HH:mm:ss')+ ' GMT';
        system.debug('formattedDate--'+formattedDate);
        system.debug('fileSize--'+fileSize);
        string CanonicalizedHeaders = 'x-ms-date:'+formattedDate+'\nx-ms-range:bytes=0-'+string.valueOf(fileSize-1)+'\nx-ms-version:2016-05-31\nx-ms-write:update';

        string CanonicalizedResource = '/' + storageName + '/' + shareName + '/' +fileName + '\ncomp:range';
        string StringToSign = 'PUT\n\n\n' + string.valueOf(fileSize) + '\n\n\n\n\n\n\n\n\n' + CanonicalizedHeaders+'\n'+CanonicalizedResource;
        system.debug('StringToSign--'+StringToSign);

        system.debug('StringToSign--'+StringToSign);

        Blob temp = EncodingUtil.base64Decode(storageKey);
        Blob hmac = Crypto.generateMac('HmacSHA256',Blob.valueOf(StringToSign),temp ); 
        HttpRequest req = new HttpRequest();
        req.setMethod('PUT');
        req.setHeader('x-ms-version','2016-05-31' );
        req.setHeader('x-ms-date', formattedDate);
        req.setHeader('x-ms-range','bytes=0-'+string.valueOf(fileSize-1));
        req.setHeader('x-ms-write','update');
        req.setHeader('Content-Length', string.valueOf(fileSize));
        string signature = EncodingUtil.base64Encode(hmac);
        string authHeader =  'SharedKey ' + storageName +':'+signature;
        req.setHeader('Authorization',authHeader);
        req.setEndpoint('https://' + storageName + '.file.core.windows.net/' + shareName + '/' + fileName + '?comp=range');
        req.setBodyAsBlob(img);
        Http http = new Http();
        HTTPResponse res;
        res = http.send(req);     
        string responseBody = res.getBody();
        system.debug('responseBody--'+responseBody);
    }
}
3
sguler On

The error you are getting states that Content-Length value is unexpected. Likely your payload you are sending does not match 2224 bytes. Check whether setBodyAsBlob really sends 2224 byte of data. You can verify this capturing packets on a tool like WireShark.