简体   繁体   English

在 Salesforce APEX 中实施 Azure Put Blob

[英]Implementing Azure Put Blob in Salesforce APEX

I am trying to implement Azure Put Blob in Salesforce APEX but getting following error.我正在尝试在 Salesforce APEX 中实施 Azure Put Blob,但出现以下错误。 I have read almost all Microsoft documentation and tried many way but in the end stuck in same point.我已经阅读了几乎所有的 Microsoft 文档并尝试了很多方法,但最终还是停留在同一点上。

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?我仍然收到此错误,我注意到一件事,根据调试,我的内容长度为 996,但错误地显示为 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>

One issue I see in your code is that you have not included the content-length and content-type in your signature string (sts):我在您的代码中看到的一个问题是您没有在签名字符串 (sts) 中包含content-lengthcontent-type

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 .此外,考虑到您正在执行Put Blob操作而不是Put Block操作,您不需要在sts包含blockid:YmxvY2stMQ==\\ncomp:block

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 .有关更多详细信息,请参阅: https : //docs.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key

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"以下是“使用 Salesforce APEX 上传到 Azure Blob”的完整工作实现

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文件上传控制器 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辅助 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 Azure服务

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谢谢,赞恩

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM