0

I'm trying to do a POST call to SBA from Salesforce that involves uploading a file. The call asks for 4 parameters in the form of JSON. As I have tested, 3 of the 4 JSON fields are passing validation on the other end. However, when I use the VersionData(Blob) field on the ContentVersion, the error in the title is returned. I have also tried using PathOnClient(String) as the value, but receive the same error.

Perhaps there is another field on the ContentVersion object that I should be passing in to represent the file? Or maybe there is another object I should be using to retrieve the file and utilize it in Apex? My sandbox is fairly new so the Attachment object unfortunately doesn't retrieve any files via SOQL. My code is below:

public static void makeCallOutForUpload(){
    
    //query for File
    ContentVersion file = [SELECT Title, PathOnClient, VersionData FROM ContentVersion WHERE ContentDocumentId = '0693J0000007rltQAA' AND IsLatest = true];
    
    
    //sample request for upload doc callout
    String jsonstring = '{'
                       +'"name": "delete these todos.csv",'
                       +'"document": "'+file.VersionData+'",'
                       +'"document_type": "1",'
                       +'"etran_loan": "7ab138f3-7507-47f9-a330-1a2c0b4e4f73"'
                       + '}';
  
    HttpResponse response;
    ETranDetails__c etranDetails = ETranDetails__c.getOrgDefaults();
    
    //call out logic here
    Http http = new Http();
    HttpRequest request = new HttpRequest();
    request.setHeader('Authorization', etranDetails.API_Key__c);
    request.setHeader('Vendor-Key', etranDetails.Vendor_Key__c);
    request.setHeader('Content-Type', 'application/json');
    request.setEndpoint(etranDetails.Forgiveness_Endpoint_For_Upload_Docs__c);
    request.setMethod('POST');
    request.setTimeout(120000);
    request.setBody(jsonstring);
    response = http.send(request);
    
    system.debug('what came back?'+response.getStatusCode()+' '+response.getBody());
    
}
0

With the assistance of an unnamed source and these links:

http://enreeco.blogspot.com/2013/01/salesforce-apex-post-mutipartform-data.html

How can I compose a multipart/form-data request?

, I was able to figure it out. Here is the code that returned back a positive response:

Public Static blob build_SBA_Blob(string fileName, string docID, string eTranSlug, blob contVers) {
            
        //query for File
        ContentVersion file = [SELECT Title, PathOnClient, VersionData FROM ContentVersion WHERE ContentDocumentId = '0693J0000007xV8QAI' AND IsLatest = true];
            
         docID = '3'; 
         eTranSlug = 'etran number';
         fileName = file.PathOnClient; 
         contVers = file.VersionData;   
            
        //STEP X1: Create the header of the form-data
        system.debug('STEP X1: Create the header of the form-data');
        string strHeader;
        strHeader =  '--1ff13444ed8140c7a32fc4e6451aa76d\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\n'+fileName;
        strHeader += '\r\n--1ff13444ed8140c7a32fc4e6451aa76d\r\nContent-Disposition: form-data; name=\"document_type\"\r\n\r\n'+docID;
        strHeader += '\r\n--1ff13444ed8140c7a32fc4e6451aa76d\r\nContent-Disposition: form-data; name=\"etran_loan\"\r\n\r\n'+eTranSlug;
        strHeader += '\r\n--1ff13444ed8140c7a32fc4e6451aa76d\r\nContent-Disposition: form-data; name=\"document\"; filename=\"' + fileName + '\"\r\n\r\n';
        system.debug(strHeader);
        //STEP X2: Remove padding spaces from header
        system.debug('STEP X2: Remove padding spaces from header');
        String headerEncoded = EncodingUtil.base64Encode(Blob.valueOf(strHeader));
        while(headerEncoded.endsWith('='))
        {
           strHeader+=' ';
           headerEncoded = EncodingUtil.base64Encode(Blob.valueOf(strHeader));
        }
        //STEP X3: Create the body and footer, handling the padding spaces
        system.debug('STEP X3: Create the body and footer, handling the padding spaces');
        string strFooter = '--1ff13444ed8140c7a32fc4e6451aa76d--'; 
        String bodyEncoded = EncodingUtil.base64Encode(contVers);
               Blob bodyBlob = null;
               String last4Bytes = bodyEncoded.substring(bodyEncoded.length()-4,bodyEncoded.length());
         // GW: replacement section to get rid of padding without corrupting data
         if(last4Bytes.endsWith('==')) {
            system.debug('last4Bytes.endsWith(\'==\')');
            // The '==' sequence indicates that the last group contained only one 8 bit byte
            // 8 digit binary representation of CR is 00001101
            // 8 digit binary representation of LF is 00001010
            // Stitch them together and then from the right split them into 6 bit chunks
            // 0000110100001010 becomes 0000 110100 001010
            // Note the first 4 bits 0000 are identical to the padding used to encode the
            // second original 6 bit chunk, this is handy it means we can hard code the response in
            // The decimal values of 110100 001010 are 52 10
            // The base64 mapping values of 52 10 are 0 K
            // See http://en.wikipedia.org/wiki/Base64 for base64 mapping table
            // Therefore, we replace == with 0K
            // Note: if using nn instead of rn replace == with 'oK'
            last4Bytes = last4Bytes.substring(0,2) + '0K';
            bodyEncoded = bodyEncoded.substring(0,bodyEncoded.length()-4) + last4Bytes;
            // We have appended the rn to the Blob, so leave footer as it is.
            String footerEncoded = EncodingUtil.base64Encode(Blob.valueOf(strFooter));
            //PUT IT ALL TOGETHER IN THE BLOB
            bodyBlob = EncodingUtil.base64Decode(headerEncoded+bodyEncoded+footerEncoded);
          } else if(last4Bytes.endsWith('=')) {
            system.debug('last4Bytes.endsWith(\'=\')');
            // '=' indicates that encoded data already contained two out of 3x 8 bit bytes
            // We replace final 8 bit byte with a CR e.g. r
            // 8 digit binary representation of CR is 00001101
            // Ignore the first 2 bits of 00 001101 they have already been used up as padding
            // for the existing data.
            // The Decimal value of 001101 is 13
            // The base64 value of 13 is N
            // Therefore, we replace = with N
            // Note: if using n instead of r replace = with 'K'
            last4Bytes = last4Bytes.substring(0,3) + 'N';
            bodyEncoded = bodyEncoded.substring(0,bodyEncoded.length()-4) + last4Bytes;
            // We have appended the CR e.g. r, still need to prepend the line feed to the footer
            strFooter = 'n' + strFooter;
            String footerEncoded = EncodingUtil.base64Encode(Blob.valueOf(strFooter));
            //PUT IT ALL TOGETHER IN THE BLOB
            bodyBlob = EncodingUtil.base64Decode(headerEncoded+bodyEncoded+footerEncoded);             
          } else {
            system.debug('else');
            // Prepend the CR LF to the footer
            strFooter = 'rn' + strFooter;
            String footerEncoded = EncodingUtil.base64Encode(Blob.valueOf(strFooter));
            //PUT IT ALL TOGETHER IN THE BLOB
            bodyBlob = EncodingUtil.base64Decode(headerEncoded+bodyEncoded+footerEncoded); 
          }
            
      ETranDetails__c etranDetails = ETranDetails__c.getOrgDefaults();
            
      HttpRequest req = new HttpRequest();
      req.setHeader('Content-Type','multipart/form-data; charset="UTF-8"; boundary="1ff13444ed8140c7a32fc4e6451aa76d"');
      req.setHeader('Authorization', etranDetails.API_Key__c);
      req.setHeader('Vendor-Key', etranDetails.Vendor_Key__c);      
      req.setMethod('POST');
      req.setEndpoint(etranDetails.Forgiveness_Endpoint_For_Upload_Docs__c);
      req.setBodyAsBlob(bodyBlob);
      req.setTimeout(120000);
            
      Http http = new Http();
      HTTPResponse res = http.send(req);      
            
      system.debug('what came back?'+res.getStatusCode()+' '+res.getBody());      
            
        return bodyBlob;
    }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.