Внедрение Azure Помещение BLOB в Salesforce APEX - PullRequest
0 голосов
/ 15 марта 2020

Я пытаюсь реализовать Azure Поместить BLOB-объект в Salesforce APEX, но получаю следующую ошибку. Я прочитал почти всю документацию Microsoft и перепробовал много способов, но в конце концов застрял в одной и той же точке.

Я хотел бы, чтобы кто-то посмотрел, пожалуйста.

Ошибка

<?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>

Ниже приведен пример кода, ключевая информация для моей тестовой среды, поэтому важно отметить, что вы можете использовать тот же ключ для выполнения примера.

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);
    }
    }

Второй подход После редактирования моего кода для генерации подписи я получаю сейчас эту ошибку

<?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>

Ниже приведен мой код

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);
        }
}

Третий подход Это три различных способа, которые я использую для подписи

 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';

Я все еще получаю эту ошибку, Я заметил, что длина моего контента составляет 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>

Ответы [ 2 ]

0 голосов
/ 19 марта 2020

Проведя так много дней, я смог разобраться в проблеме. Это было с длиной файла.

Ниже приводится полная рабочая реализация «Загрузить в Azure Blob с Salesforce APEX»

Компонент молнии

<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');
        }
    }

})

Помощник 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());

   }

}

Я был бы рад, если бы кто-нибудь из этого поста переделал код более чистым способом.

Надеюсь, это вам пригодится, ребята

Спасибо, Зейн

0 голосов
/ 15 марта 2020

Одна проблема, которую я вижу в вашем коде, заключается в том, что вы не включили content-length и content-type в строку подписи (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';

Кроме того, учитывая, что вы выполняете Put Blob операция, а не Put Block операция, вам не нужно включать blockid:YmxvY2stMQ==\ncomp:block в sts.

Это должно быть:

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;

Для получения более подробной информации, пожалуйста, смотрите: https://docs.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key.

...