[SalesForce] How to save large files in Salesforce with synchronous Apex

I've created a Visualforce page that allows users to drag and drop files and have them uploaded to Salesforce and then displayed in a list immediately. To do so, my VF page sends the file in chunks via JavaScript Remoting and my Apex controller saves the chunks as records of a custom object. When all the chunks have been transmitted, the controller pulls all the chunks from the DB, concatenates them, and saves them as a ContentVersion. The controller then returns the file info to the page for display.

This works fine for smaller files, but for files of any significant size I run into the heap size limit of 6MB as the apex tries to assemble the chunks in memory. So I'm wondering if anyone has pioneered a creative way around this limit.

I'm aware that I could get a somewhat higher limit (12MB) if I used asynchronous Apex, but I need a synchronous operation to return file info to the page (and 12MB is still inadequate).

One thought I had was that I could save each chunk directly to the ContentVersion.VersionData, and then somehow through DML do updates containing just the next chunk to concatenate to the existing VersionData, thus avoiding assembling the chunks in memory. But I realize this is probably wishful thinking, as I see no way to do an update except to query the DB, concatenate one chunk to another in memory, then perform an update.

Anyone have a solution?

UPDATE: here are the relevants bits of my controller code

@remoteAction
global static File_Chunk__c saveChunk(String chunk, String fileIdentifier) {
    File_Chunk__c fc = new File_Chunk__c();
    fc.File_Identifier__c = fileIdentifier;
    fc.Chunk__c = chunk;
    insert fc;
    return fc;
}

@remoteAction
global static ContentDocumentLink assembleChunksSaveFile(String fileIdentifier, String title, String entity, String fdType) {
    List<File_Chunk__c> fcl = [SELECT Chunk__c FROM File_Chunk__c WHERE File_Identifier__c = :fileIdentifier ORDER BY Name ASC];
    String body = '';
    for (File_Chunk__c fc : fcl) {
      body += fc.Chunk__c;
    }

    ContentVersion cv = new ContentVersion();
    cv.Title = title;
    cv.VersionData = EncodingUtil.base64Decode(body);
    cv.PathOnClient = title;
    cv.FD_Document_Type__c = fdType;

    insert cv;

    Id docId = [SELECT ContentDocumentId FROM ContentVersion WHERE Id = :cv.Id].ContentDocumentId;

    ContentDocumentLink cdl = new ContentDocumentLink();
    cdl.ContentDocumentId = docId;
    cdl.LinkedEntityId = entity;
    cdl.ShareType = 'V';
    insert cdl;

    ContentDocumentLink fileinfo = [SELECT LinkedEntityId, LinkedEntity.Name, ContentDocumentId, ContentDocument.Title, ContentDocument.ContentSize, ContentDocument.FileType, ContentDocument.LatestPublishedVersion.FD_Document_Type__c FROM ContentDocumentLink WHERE Id = :cdl.Id];

    return fileinfo;
}

Best Answer

You Can create ContentVersion more than 25MB using AJAX Toolkit i.e, sforce object.

Please find the official documentation here

Below is the sample code to create an account record:

var account = new sforce.SObject("Account");//Similarly can be created for ContentVersion
account.Name = "my new account";//Give Required Fields
var result = sforce.connection.create([account]);

if (result[0].getBoolean("success")) {
  log("new account created with id " + result[0].id);
} else {
  log("failed to create account " + result[0]);
}
Related Topic