1

I have used Multipart/message to send Large size of file from Salesforce to External system. but still I got the error as heap size limit too large, I'm able to send below 2MB file.

Source Org

public class SendAttachmentByRestAPI {
    private final String clientId = 'XXXXXXX';
    private final String clientSecret = 'YYYYYY';
    private final String username = 'salesforce.com';
    private final String password = 'password';
    public class deserializeResponse{
        public String id;
        public String access_token;
    }
    public String getAccessToken ()
    {
        String reqbody = 'grant_type=password' + '&client_id='+clientId +
                        '&client_secret='+clientSecret + '&username='+username + '&password='+password;
        Http h = new Http();
        HttpRequest req = new HttpRequest();
        req.setBody(reqbody);
        system.debug('reqbody'+reqbody);
        req.setMethod('POST');
        req.setEndpoint('https://login.salesforce.com/services/oauth2/token');
        HttpResponse res = h.send(req);
        deserializeResponse response = (deserializeResponse)JSON.deserialize(res.getbody(),deserializeResponse.class);
        system.debug('@@@@access_token@@'+response );
        return response.access_token;
    }
    public static void sendAttachement(){
        //get Access Token
        SendAttachmentByRestAPI accessTk = new SendAttachmentByRestAPI();
        String accessToken;
        accessToken = accessTk.getAccessToken();
        System.debug('accessToken==='+accessToken); 
        
        Set<Id> contentDocIds = new Set<Id>();
        for (ContentDocumentLink cdl : [SELECT ContentDocumentId FROM ContentDocumentLink WHERE LinkedEntityId IN (SELECT Id FROM Opportunity WHERE StageName = 'Closed Won')]) {
             contentDocIds.add(cdl.ContentDocumentId);
        } 
        ContentVersion cvList = [SELECT Id,VersionData,Title,FileExtension, PathOnClient,ContentDocumentID FROM ContentVersion WHERE ContentDocumentId IN :contentDocIds limit 1];
       
      String boundary = '----------------------------741e90d31eff';
        String header = '--'+boundary+'nContent-Disposition: form-data; name="file";nContent-Type: application/octet-stream';
        // GW: Does not prepend footer by nn, we will see why in a second
        // String footer = 'rn--'+boundary+'--';
        String footer = '--'+boundary+'--';        
        String headerEncoded = EncodingUtil.base64Encode(Blob.valueOf(header+'rnrn'));
        while(headerEncoded.endsWith('='))
        {
            header+=' ';
            headerEncoded = EncodingUtil.base64Encode(Blob.valueOf(header+'rnrn'));
        }
        String bodyEncoded = EncodingUtil.base64Encode(cvList.VersionData);
        // GW: Do not encode footer yet
        // String footerEncoded = EncodingUtil.base64Encode(Blob.valueOf(footer));
        Blob bodyBlob = null;
        String last4Bytes = bodyEncoded.substring(bodyEncoded.length()-4,bodyEncoded.length());
        // GW: Replacing this entire section
        /*
        if(last4Bytes.endsWith('='))
        {
            Blob decoded4Bytes = EncodingUtil.base64Decode(last4Bytes);
            HttpRequest tmp = new HttpRequest();
            tmp.setBodyAsBlob(decoded4Bytes);
            String last4BytesFooter = tmp.getBody()+footer;  
            bodyBlob = EncodingUtil.base64Decode(headerEncoded+bodyEncoded.substring(0,bodyEncoded.length()-4)+EncodingUtil.base64Encode(Blob.valueOf(last4BytesFooter)));
        }
        else
        {
            bodyBlob = EncodingUtil.base64Decode(headerEncoded+bodyEncoded+footerEncoded);
        }
       */
        // GW: substitution area to dispose of cushioning without debasing information
        if(last4Bytes.endsWith('==')) {
            last4Bytes = last4Bytes.substring(0,2) + '0K';
            bodyEncoded = bodyEncoded.substring(0,bodyEncoded.length()-4) + last4Bytes;       
            String footerEncoded = EncodingUtil.base64Encode(Blob.valueOf(footer));
            bodyBlob = EncodingUtil.base64Decode(headerEncoded+bodyEncoded+footerEncoded);
        } else if(last4Bytes.endsWith('=')) {
            // '=' demonstrates that encoded information previously contained two out of 3x 8 digit bytes
            // for the existing data.
            // The Decimal value of 001101 is 13
            // The base64 value of 13 is N
            // Therefore, we replace = with N       
            last4Bytes = last4Bytes.substring(0,3) + 'N';
            bodyEncoded = bodyEncoded.substring(0,bodyEncoded.length()-4) + last4Bytes;
            // We have appended the CR e.g. r, 
            footer = 'n' + footer;
            String footerEncoded = EncodingUtil.base64Encode(Blob.valueOf(footer));
            bodyBlob = EncodingUtil.base64Decode(headerEncoded+bodyEncoded+footerEncoded);         
        } else {
            // Prepend the CR LF to the footer
            footer = 'rn' + footer;
            String footerEncoded = EncodingUtil.base64Encode(Blob.valueOf(footer));
            bodyBlob = EncodingUtil.base64Decode(headerEncoded+bodyEncoded+footerEncoded); 
        }
      
        
        if(accessToken != Null){
            
            Http h = new Http();
            HttpRequest req = new HttpRequest();
            req.setEndpoint('https://datasirpicnat-dev-ed.my.salesforce.com/services/apexrest/getContentVersions/');
            req.setMethod('POST');
            req.setBody(
                JSON.serialize(new map<String, String>{
                    'Title' => 'xyz.txt',  
                    'VersionData' => EncodingUtil.base64Encode(cvList.VersionData),
                    'FileExtension' => '.txt'  
                 })
            );
            req.setHeader('Authorization','Bearer '  +accessToken);
            req.setTimeout(60000);
            req.setHeader('Content-Type','application/json');
            req.setHeader('Content-Type','multipart/form-data; boundary='+boundary);
         //   req.setHeader('Content-Length',String.valueof(cvList.VersionData.length()));
            HttpResponse res = h.send(req);
            system.debug('res'+res.getBody());  
     
            list<Document_URL__c> a = [SELECT Id,Name,Document_URL__c FROM Document_URL__c limit 1];
            for(Document_URL__c b : a){
            b.Document_URL__c = res.getBody();
            b.name = cvList.Title;
            system.debug('b.Document_URL__c'+b.Document_URL__c);
            update a;
            system.debug('a'+a); 
            } 
        } 
    }   
}

1 Answer 1

3

There is a limit of 6MB for the request body that can be sent via the apex outbound.

Note that for multipart messages you are hitting this limit of around 2MB since the size after you creating your own string via custom code is exceeding 6MB. As it involves base64 encode and this adds additional overhead.

For this type of integration, I suggest using a middleware tool like Mulesoft or building a Heroku app that salesforce can call into Salesforce and the app can extract files and do a multipart callout instead of performing Multipart request via apex.

When Salesforce Functions will be in GA, this will be the ideal use case for the Salesforce Functions.

1
  • Thanks for your reply Mohith, Can u suggest to enhance my code other than base64 to send large file to Target Org.
    – user102524
    Commented Aug 18, 2021 at 8:05

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .