18

Are there any work arounds for this issue?

I'm trying to upload a file(attachment) to an 3rd party REST API Service. It works fine until my file sizes get about ~5 mb. Then I start getting the following error:

common.apex.runtime.impl.ExecutionException: String length exceeds maximum: 6000000

The code that I'm using is a little hacky due to the requirements of the external service (must be multipart, does not accept base64).

Pretty much it takes the contents of the attachment blob, and combines them with the multipart header then puts them back into a blob.

Here's the code I'm using. There are some statics that I left out but you get the point.

public static void UploadAttachment(Attachment attachment, String folder){

    String boundary = '----------------------------741e90d31eff';
    String header = '--'+boundary+'\nContent-Disposition: form-data; name="file"; filename="'+attachment.name+'";\nContent-Type: application/octet-stream';
    String footer = '\r\n--'+boundary+'--';              
    String headerEncoded = EncodingUtil.base64Encode(Blob.valueOf(header+'\r\n\r\n'));
    while(headerEncoded.endsWith('=')){
        header+=' ';
        headerEncoded = EncodingUtil.base64Encode(Blob.valueOf(header+'\r\n\r\n'));
    }
    String bodyEncoded = EncodingUtil.base64Encode(attachment.body);
    String footerEncoded = EncodingUtil.base64Encode(Blob.valueOf(footer));

    Blob bodyBlob = null;
    String last4Bytes = bodyEncoded.subString(bodyEncoded.length()-4,bodyEncoded.length());
    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);
    }

    HttpRequest req = new HttpRequest();
    req.setHeader('Authorization', authorizationData);
    req.setHeader('Content-Type','multipart/form-data; boundary='+boundary);
    req.setMethod('POST');
    req.setEndpoint(server+storageService+'/'+folder);
    req.setBodyAsBlob(bodyBlob);
    req.setTimeout(120000);

    Http http = new Http();
    HTTPResponse res = http.send(req);
}

Code modified from this blog post

  • 3
    Is this an future method? If not, I think this limit is there as a quick way to avoid going over the heap size of 6MB. It's 12mb in future and batch contexts. – ca_peterson Mar 1 '14 at 1:49
  • Agreed. Check salesforce.com/us/developer/docs/apexcode/Content/… for more limit information. I don't know why you didn't hit the 3MB limit on HTTP requests - ("Maximum size of callout request or response (HTTP request or Web services call)" from the URL I referenced - so that's pretty interesting. – DavidSchach Mar 1 '14 at 17:21
  • Perhaps the 6M string length limit already corresponds to the 12Mbyte heap limit as AFAIK an Apex string is a JVM string using 2 bytes per character. But as David mentions there are other limits to run foul of in any case. Apex doesn't have streaming IO classes so large data volumes are always going to be problematic. – Keith C Mar 1 '14 at 23:27
  • 1
    May be you should use third party ETL tool like Cast Iron to pull your attachment from SFDC and push it to your destination system. If you do not want to invest in ETL tool then you can write your won scrip that will pull this attachments from SFDC and push it into destination system using the API. – AtulRajguru9 Apr 28 '14 at 5:58
  • 1
    The simplest solution ended up being proxying it through a Node server. Just pipe the response straight back to saleforce. – NSjonas Apr 29 '14 at 19:57
5

Unfortunately your SOL when it comes to that sort of limit, you're only route is going to be moving the operation off Salesforce.

One option would be to create a heroku app with a single endpoint that takes the attachment Id. The endpoint can then retrieve the attachment and then upload it to the 3rd party server.

  • 3
    crazy... that's exactly what I ended up doing ;) – NSjonas Jul 17 '14 at 20:00
  • @Ralph Callaway, I am facing same error how can i do same thing using heroku app, can you provide me any sample code for this. I am sending document using REST API into netdocuments, but it document is too large then i am getting "String length exceeds maximum: 12000000" this error. I know this is salesforce string limit and we can't bypass it but the way you told is seems possible to send document by bypassing this error using heroku app. – Sandeep Kumar Singh Oct 25 '16 at 12:41
-1

What is the source of the file you want to send to external system??

Sending whole file in once is not a good idea.

You can make 10 callouts in a context so that now you can break file into pieces and update external files content.

1) first piece will create file 2) all other pieces will just update file.

Thanks

  • I don't have control over the external API I'm calling. Not quite sure what your getting at but the entire file must be sent in a single http post. I cant "update" it once its uploaded. – NSjonas Jun 16 '14 at 18:28
  • I dont know what kind of community is this,where I am just trying to help and people just give negative without any reason. There must be some kind of rules for this as this habit discourage people like me. – ashishcloud Jun 18 '14 at 6:28
  • Hi NSjonas, Salesforce have some limits and you must have to respect them. So if you need to send data from callout more then the limit ,you have to break it and send it in parts. In case of files,all of the external file system have REST request option for both insert and update. So you can first create a file with first chunk and then can update it with all other chunks. When youo will send your last chunk, It will create a full valid file in external system. This is tested and used by me. Do some efforts and you will get result. – ashishcloud Jun 18 '14 at 6:33
  • What do you mean by "all of the external file system have REST request option for both insert and update"? What "external file systems" are you talking about? The API I'm coding against certainly does not. I'm making an HTTP POST to a 3rd party API service. I downvoted your answer because it did not address the question, was lacking detail, and was hard to understand due to grammar. Posting sample code might make your answer more understandable, but I think you are missing the point. – NSjonas Jun 18 '14 at 18:10
  • Which Api you are using?? – ashishcloud Jun 19 '14 at 8:50

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.