Implementing Azure Put Blob in Salesforce APEX

匆匆过客 提交于 2020-04-18 06:09:07

问题


I am trying to implement Azure Put Blob in Salesforce APEX but getting following error. I have read almost all Microsoft documentation and tried many way but in the end stuck in same point.

I would like someone to have a look please.

Error

<?xml version="1.0" encoding="utf-8"?>
<Error>
    <Code>AuthenticationFailed</Code>
    <Message>Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
RequestId:e41a64e6-301e-0047-5cbd-fa2206000000
Time:2020-03-15T11:36:39.3690495Z</Message>
    <AuthenticationErrorDetail>The MAC signature found in the HTTP request '7XBxAbAYZNkrx6mWol+RAYieDNZm9/JN6/4IQ+ygxhk=' is not the same as any computed signature. Server used following string to sign: 'PUT


1002








x-ms-blob-type:BlockBlob
x-ms-date:Sun, 15 Mar 2020 11:31:59 GMT
x-ms-version:2015-02-21
/zaindevtesting/zaindevblob/test.txt
blockid:YmxvY2stMQ==
comp:block'.</AuthenticationErrorDetail>
</Error>

Following is the Sample code the key information is for my test env so noting important, you can use same key to execute the example.

public static void putAzureBlob(Blob fileBody)
    {

        String blobName = '/zaindevtesting/zaindevblob/test.txt';
        String urlQueue = 'https://zaindevtesting.blob.core.windows.net/zaindevblob/test.txt';

        string storageKey = 'xxxx==';

       Datetime dt = Datetime.now();
    string formattedDate = dt.formatGMT('EEE, dd MMM yyyy HH:mm:ss') + ' GMT';
    System.debug('formattedDate--->'+formattedDate);
    System.debug('fileLength--->'+ fileLength);
    System.debug('fileLength--->'+ fileType);
    string sts = 'PUT\n\n\n'+fileLength+'\n\n'+fileType+'\n\n\n\n\n\nx-ms-blob-type:BlockBlob\nx-ms-date:' + formattedDate + '\nx-ms-version:2015-02-21\n' + blobName;

    String signature = '';
    Blob unicodeKey = EncodingUtil.base64Decode(storageKey);
    Blob data = Crypto.generateMac('HMACSHA256', Blob.valueOf(sts), unicodeKey);
    signature = EncodingUtil.base64Encode(data);

    string authHeader = 'SharedKey zaindevtesting:' + signature;
    System.debug('authHeader--->'+authHeader);
    HttpRequest req = new HttpRequest();
    req.setMethod('PUT');
   req.setHeader('x-ms-blob-type', 'BlockBlob');
    req.setHeader('x-ms-date', formattedDate);
    req.setHeader('x-ms-version', '2015-02-21');
    req.setHeader('Authorization', authHeader);

    req.setEndpoint(urlQueue);
    req.setBodyAsBlob(fileBody);

    Http http = new Http();

    try
    {
    HTTPResponse res = http.send(req);
    // in the response body you have your blob
    System.debug('Status--->'+res.getStatus());
    // Let's save it as attachment

    System.debug('Body---->'+res.getBody());

    }catch(Exception exp)
    {
        System.debug('Exception --->'+exp);
    }
    }

Second Approach After editing my code for generating Signature I am getting now this Error

<?xml version="1.0" encoding="utf-8"?>
<Error>
<Code>InvalidAuthenticationInfo</Code>
<Message>Authentication information is not given in the correct format. Check the value of Authorization header.
RequestId:c869405b-501e-0013-6328-fbc88c000000
Time:2020-03-16T00:16:06.8896380Z</Message>
</Error>

Following is my Code

public static void putAzureBlob(Blob fileBody, Integer fileLength, String fileType)
    {

        String blobName = '/zaindevtesting/zaindevblob/test.txt';
        String urlQueue = 'https://zaindevtesting.blob.core.windows.net/zaindevblob/test.txt';

        string storageKey = 'XXXXXXXXXXXX==';

        Datetime dt = Datetime.now();
        string formattedDate = dt.formatGMT('EEE, dd MMM yyyy HH:mm:ss') + ' GMT';
        System.debug('formattedDate--->'+formattedDate);
        System.debug('fileLength--->'+ fileLength);
        System.debug('fileLength--->'+ fileType);
        string sts = 'PUT\n\n\n'+fileLength+'\n\n'+fileType+'\n\n\n\n\n\nx-ms-blob-type:BlockBlob\nx-ms-date:' + formattedDate + '\nx-ms-version:2015-02-21\n' + blobName;

        String signature = '';
        Blob unicodeKey = EncodingUtil.base64Decode(storageKey);
        Blob data = Crypto.generateMac('HMACSHA256', Blob.valueOf(sts), unicodeKey);
        signature = EncodingUtil.base64Encode(data);
        System.debug('signature--->1'+ signature);

        signature = EncodingUtil.urlEncode(signature, 'UTF-8');

         System.debug('signature--->2'+ signature);

        string authHeader = 'SharedKey zaindevtesting:' + signature;

        System.debug('authHeader--->'+authHeader);
        HttpRequest req = new HttpRequest();

       req.setMethod('PUT');
       req.setHeader('x-ms-blob-type', 'BlockBlob');
       req.setHeader('x-ms-date', formattedDate);
       req.setHeader('x-ms-version', '2015-02-21');
       req.setHeader('Authorization', authHeader);
       req.setHeader('Content-Length', String.valueof(fileLength));


        req.setEndpoint(urlQueue);
        req.setBodyAsBlob(fileBody);

        Http http = new Http();

        try
        {
        HTTPResponse res = http.send(req);
        // in the response body you have your blob
        System.debug('Status--->'+res.getStatus());
        // Let's save it as attachment

        System.debug('Body---->'+res.getBody());

        }catch(Exception exp)
        {
            System.debug('Exception --->'+exp);
        }
}

Third Approach These are the three different way I am using for Signature

 string sts = 'PUT\n\n\n'+fileLength+'\n\n'+fileType+'\n\n\n\n\n\n\nx-ms-blob-type:BlockBlob\nx-ms-date:' + formattedDate + '\nx-ms-version:2015-02-21\n' + blobName;

 //string sts = 'PUT\n\n\n'+fileLength+'\n\n'+fileType+'\n\n\n\n\n\n\nx-ms-date:' + formattedDate + '\nx-ms-version:2015-02-21\n' + blobName;

 // string sts = 'PUT\n\n\n\n\n\n\n\n\n\n\n\nx-ms-date:' + formattedDate + '\nx-ms-version:2015-02-21\n' + blobName+'\nrestype:container\ntimeout:30';

I am getting this error still, one thing I have notice that my content length is 996 as per debug but in error it is showing 1002?

<?xml version="1.0" encoding="UTF-8"?>
<Error>
   <Code>AuthenticationFailed</Code>
   <Message>Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
RequestId:f7ccfaf6-a01e-0060-113d-fbb84f000000
Time:2020-03-16T02:52:05.0980775Z</Message>
   <AuthenticationErrorDetail>The MAC signature found in the HTTP request 'R+k2bivFW2AH3UriO8m4z8RxPJRDC8uujRc6FCZMEs8=' is not the same as any computed signature. Server used following string to sign: 'PUT


1002

text/plain;charset=UTF-8






x-ms-blob-type:BlockBlob
x-ms-date:Mon, 16 Mar 2020 02:52:04 GMT
x-ms-version:2015-02-21
/zaindevtesting/zaindevblob/test.txt'.</AuthenticationErrorDetail>
</Error>

回答1:


One issue I see in your code is that you have not included the content-length and content-type in your signature string (sts):

string sts = 'PUT\n\n\n\n\n\n\n\n\n\n\nx-ms-blob-type:BlockBlob\nx-ms-date:' + formattedDate + '\nx-ms-version:2015-02-21\n' + blobName+'\nblockid:YmxvY2stMQ==\ncomp:block';

Also, considering you're performing Put Blob operation and not Put Block operation, you don't need to include blockid:YmxvY2stMQ==\ncomp:block in sts.

It should be:

string sts = 'PUT\n\n\n' + content-length-here (745 based on the file you're trying to upload) + '\n\n' + content-type-here (text/plain based on the file you're trying to upload + '\n\n\n\n\n\nx-ms-blob-type:BlockBlob\nx-ms-date:' + formattedDate + '\nx-ms-version:2015-02-21\n' + blobName;

For more details, please see this: https://docs.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key.




回答2:


After spending so many days, I was able to figure out the issue. It was with the length of the file.

Following is the complete working implementation of "Upload to Azure Blob with Salesforce APEX"

Lightning Component

<aura:component controller="FileUploadController" implements="flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,force:lightningQuickAction" access="global" >

    <aura:attribute name="azureservice" type="Object" />
    <aura:attribute name="accept" type="List" default="['.jpg', '.jpeg', '.pdf', '.Docx', '.Doc']"/>
    <aura:attribute name="multiselect" type="Boolean" default="true"/>
    <aura:attribute name="disabled" type="Boolean" default="false"/>

    <aura:attribute name="fileName" type="String" default="No File Selected.." />

    <lightning:card variant="Narrow" title="File Upload">     
        <!-- Lightning Input with file type and on file change call the 'handleFilesChange' controller -->
        <div style="padding 10px">
            <lightning:input aura:id="fileId" onchange="{!c.handleFiles}" type="file" name="file" label="Upload File" multiple="true"/>
            <div class="slds-text-body_small slds-text-color_error">{!v.fileName} </div>
            <br/>

            <div class="slds-is-relative">
                <lightning:spinner aura:id="Spinner" alternativeText="Loading..." size="small" class="slds-hide"/>
            </div>
        </div>

        <lightning:buttonGroup>
            <div class="slds-m-top_medium">
                <lightning:button label="Cancel" onclick="{!c.handleCancel}" class="slds-m-top--medium"/>
            </div>
            <div class="slds-m-top_medium">
                <lightning:button label="Save" onclick="{!c.handleUpload}" class="slds-m-top--medium" />
            </div>
        </lightning:buttonGroup>
    </lightning:card>
</aura:component>

FileUploadController Javascript

({


     handleFiles: function(component, event, helper) {
        var fileName = 'No File Selected..';
        if (event.getSource().get("v.files").length > 0) {
            console.log('fileName--->'+fileName)
            fileName = event.getSource().get("v.files")[0]['name'];
        }
        component.set("v.fileName", fileName);
    },

    handleUpload: function(component, event, helper) {
        if (component.find("fileId").get("v.files").length > 0) {
            console.log('No of files-->'+component.find("fileId").get("v.files").length);

            //Calling helper method to upload the file
           //Show Spinner while request is in process
            helper.showSpinner(component);

            helper.uploadHelper(component, event);

            helper.hideSpinner(component);

        } else {
            alert('Please Select a Valid File');
        }
    }

})

Helper Javascript

({


    uploadHelper: function(component, event) {
        // start/show the loading spinner   
        component.set("v.showLoadingSpinner", true);
        // get the selected files using aura:id [return array of files]
        var fileInput = component.find("fileId").get("v.files");

        // get the first file using array index[0]  
        var file = fileInput[0];

        var fileType = file.type;
        var self = this;
        // check the selected file size, if select file size greter then MAX_FILE_SIZE,
        // then show a alert msg to user,hide the loading spinner and return from function  
        if (file.size > self.MAX_FILE_SIZE) {
           // component.set("v.showLoadingSpinner", false);
            component.set("v.fileName", 'Alert : File size cannot exceed ' + self.MAX_FILE_SIZE + ' bytes.\n' + ' Selected file size: ' + file.size);
            return;
        }

        var objFileReader = new FileReader();
        objFileReader.onload = $A.getCallback(function() {

            var fileContents = objFileReader.result;
            var base64 = 'base64,';
            var dataStart = fileContents.indexOf(base64) + base64.length;

            fileContents = fileContents.substring(dataStart);

            // call the uploadProcess method 

            self.uploadProcess(component, file, fileContents);
        });
        objFileReader.readAsDataURL(file);
    },

     uploadProcess: function(component, file, fileContents) {

        var action = component.get("c.upload"); 
        action.setParams({
            base64Data: fileContents,
            filelength: file.size,
            fileType: file.type,
            fileName: file.name

        });

        // set call back 
        action.setCallback(this, function(response) {

            var isUploaded = response.getReturnValue();
            if(isUploaded==true)
            {
                var state = response.getState();
                if (state === "SUCCESS") {
                    console.log('your File is uploaded successfully');
                    // handel the response errors        
                } else if (state === "INCOMPLETE") {
                    alert("From server: " + response.getReturnValue());
                } else if (state === "ERROR") {
                    var errors = response.getError();
                    if (errors) {
                        if (errors[0] && errors[0].message) {
                            console.log("Error message: " + errors[0].message);
                        }
                    } else {
                        console.log("Unknown error");
                    }
                }
                console.log('Your File is uploaded successfully');
            }else{
                console.log("Your File is has not uploaded successfully");
            }
        });

        $A.enqueueAction(action);


    },

     showSpinner:function(component){
        var spinnerMain =  component.find("Spinner"); 
        $A.util.removeClass(spinnerMain, "slds-hide");

    },

    hideSpinner:function(component){
        var spinnerMain =  component.find("Spinner");
        $A.util.addClass(spinnerMain, "slds-hide");

    }    
})

FileUploadController

public class FileUploadController {

      @AuraEnabled
      public static Boolean upload(String base64Data, Integer filelength, String fileType, String fileName ){


            Blob blobData = EncodingUtil.base64Decode(base64Data);

            AzureService service = new AzureService();
            Boolean isUploaded = service.uploadBlob(blobData, filelength, fileType, fileName);
           // AzureService.generateAuthorizationHeader(blobData, filelength, fileType);
            return isUploaded;
        }


    }

AzureService

public class AzureService {

    private String storageKey;
    private String storageName;
    private String storageContainer;
    private String storageUrl;
    private String blobName;
    private String requestURL;
    private String fileLength;
    private String formattedDate ;
    private String fileType;
    private String fileName;


    private final string DATEFORMAT = 'EEE, dd MMM yyyy HH:mm:ss z';
    private final string VERSION = '2015-12-11';
    private final string BLOB_TYPE = 'BlockBlob';


    public Boolean uploadBlob( Blob fileBody, Integer intFileLength, String strFileType, String strFileName)
    {

        Boolean isUploaded= false;
        this.fileName = EncodingUtil.urlEncode(strFileName, 'UTF-8');
        this.fileType = strFileType;
        this.storageName = 'YourStorageName';
        this.storageContainer = 'YourContainerName';
        this.storageKey = 'YourStorageAccountKet';
        this.storageUrl ='https://YourStorageName.blob.core.windows.net';

        this.blobName = '/'+storageName+'/'+storageContainer+'/'+fileName;
        System.debug('blobName--->'+blobName);
        this.requestURL = storageUrl+'/'+storageContainer+'/'+fileName;
        System.debug('requestURL--->'+requestURL);

        this.fileLength = String.valueof(intFileLength);

        String strSharedKey = getBlobSharedKey();

        try
        {

            this.uploadBlob(fileBody, strSharedKey);
            isUploaded = true;

        }catch(Exception exp)
        {
            System.debug('Exception occur while uploading the Blob-->'+exp.getMessage());
            isUploaded = false;
        }

        return isUploaded;
    }


    public String getBlobSharedKey()
    {
        System.debug('getBlobSharedKey--->Start');
        String sharedKey;
        String signature;
        Datetime dt = Datetime.now();

        this.formattedDate = dt.formatGMT(DATEFORMAT);
        String stringToSign = 'PUT\n\n\n'+fileLength+'\n\n'+fileType+'\n\n\n\n\n\n\nx-ms-blob-type:BlockBlob\nx-ms-date:'+formattedDate+'\nx-ms-version:2015-12-11\n'+blobName;

        System.debug('stringToSign--->'+stringToSign);

        Blob unicodeKey = EncodingUtil.base64Decode(storageKey);
        Blob data = Crypto.generateMac('HMACSHA256', Blob.valueOf(stringToSign), unicodeKey);
        signature = EncodingUtil.base64Encode(data);

        sharedKey = 'SharedKey '+storageName+':' + signature;
        return sharedKey;
    }

   public void uploadBlob(Blob fileBody, String sharedKey)
   {
       HttpRequest req = new HttpRequest();


       req.setMethod('PUT');

       req.setHeader('x-ms-blob-type', BLOB_TYPE);
       req.setHeader('x-ms-version', VERSION);
       req.setHeader('x-ms-date', formattedDate);
       req.setHeader('Authorization', sharedKey);
       req.setHeader('Content-Type', fileType);
       req.setHeader('Content-Length', fileLength);

       req.setEndpoint(this.requestURL);

       req.setBodyAsBlob(fileBody);

       Http http = new Http();
       HTTPResponse res = http.send(req);
       // in the response body you have your blob
       System.debug('Response Body--->'+res.getBody());
       System.debug('Status--->'+res.getStatus());

   }

}

I would be happy if someone this post redo the code in more clean manner.

Hope this will find useful to you guys

Thanks, Zain



来源:https://stackoverflow.com/questions/60692538/implementing-azure-put-blob-in-salesforce-apex

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!