Salesforce Developer Javascript Remoting Example3

salesforce-developer-javascript-remoting

// Salesforce - Developer - JavaScript Remoting - Example 3 - File Upload:

Using an apex:inputFile element that is bound to a variable in your controller 
is probably easier but you may have a scenario where you need to have granular 
control over how your page acts.

Benefits:

1. More granular and responsive error handling (Know the file is too big before 
   you try to upload it)

2. Keep track of upload status (you could create a progress bar for uploads if 
   you wish)

Limitations:

1. Javascript Remoting maximum message size of 1 million characters (“Input too 
   long. [0, 1000000]”)

2. Maximum String length of 6 million characters in apex code (“String length 
   exceeds maximum: 6000000”).  This means a functional limit of a file size of 
   about 4.3 MB

This example uploads an attachment to an account but can be modified to attach 
a file to a different object type fairly easily.

Consider the following Visualforce page:

<apex:page standardController="Account" extensions="JSRemotingFileUploadController" id="page">

  <script type="text/javascript">
    var maxStringSize = 6000000;  //Maximum String size is 6,000,000 characters
    var maxFileSize = 4350000;//After Base64 Encoding, this is the max file size
    var chunkSize = 950000; // Maximum Javascript Remoting message size is 
                            // 1,000,000 characters
    var attachment;
    var attachmentName;
    var fileSize;
    var positionIndex;
    var doneUploading;

// Method to prepare a file to be attached to the Account bound to the page by 
// the standardController
    function uploadFile() {
      var file = document.getElementById('attachmentFile').files[0];
      console.log(file);
      if(file != undefined) {
        if(file.size <= maxFileSize) {
          attachmentName = file.name;
          var fileReader = new FileReader();
          fileReader.onloadend = function(e) {
            attachment = window.btoa(this.result);  // Base 64 encode the file 
                                                    // before sending it
            positionIndex=0;
            fileSize = attachment.length;
            console.log("Total Attachment Length: " + fileSize);
            doneUploading = false;
            if(fileSize < maxStringSize) {
              uploadAttachment(null);
            } else {
              alert("Base 64 Encoded file is too large.  Maximum size is " + 
                maxStringSize + " your file is " + fileSize + ".");
            }

          }
          fileReader.onerror = function(e) {
            alert("There was an error reading the file.  Please try again.");
          }
          fileReader.onabort = function(e) {
            alert("There was an error reading the file.  Please try again.");
          }

          fileReader.readAsBinaryString(file);  //Read the body of the file

        } else {
          alert("File must be under 4.3 MB in size.  Your file is too large.  
            Please try again.");
        }
      } else {
        alert("You must choose a file before trying to upload it");
      }
    }

// Method to send a file to be attached to the Account bound to the page by the 
// standardController
// Sends parameters: Account Id, Attachment (body), Attachment Name, and the 
// Id of the Attachment if it exists to the controller   
    function uploadAttachment(fileId) {
      var attachmentBody = "";
      if(fileSize <= positionIndex + chunkSize) {
        attachmentBody = attachment.substring(positionIndex);
        doneUploading = true;
      } else {
        attachmentBody = attachment.substring(positionIndex, positionIndex + 
            chunkSize);
      }
      console.log("Uploading " + attachmentBody.length + " chars of " + 
        fileSize);
      JSRemotingFileUploadController.doUploadAttachment(
      '{!Account.Id}', attachmentBody, attachmentName, fileId, 
        function(result, event) {
          console.log(result);
          if(event.type === 'exception') {
            console.log("exception");
            console.log(event);
          } else if(event.status) {
            if(result.substring(0,3) == '00P') {
              if(doneUploading == true) {
                window.open("/{!Account.Id}", "_blank");
                window.location.reload();
              } else {
                positionIndex += chunkSize;
                uploadAttachment(result);
              }
            }
          } else {
            console.log(event.message);
          }
        },
        {buffer: true, escape: true, timeout: 120000}
      );
    }
  </script>

  <h2>Javascript Remoting File Upload Example </h2>
  <br /> <br />

  <input type="file" id="attachmentFile"/> <br />
  <button onclick="uploadFile()">Upload File</button>
</apex:page>

and the corresponding controller:

global with sharing class JSRemotingFileUploadController {
    public JSRemotingFileUploadController(ApexPages.StandardController c) {
    }
    @RemoteAction
    public static String doUploadAttachment(String acctId, 
      String attachmentBody, String attachmentName, String attachmentId) {
        if(acctId != null) {
            Account acct = getAccount(acctId);
            if(acct != null) {
                if(attachmentBody != null) {
                    Attachment att = getAttachment(attachmentId);
                    String newBody = '';
                    if(att.Body != null) {
                        newBody = EncodingUtil.base64Encode(att.Body);
                    }
                    newBody += attachmentBody;
                    att.Body = EncodingUtil.base64Decode(newBody);
                    if(attachmentId == null) {
                        att.Name = attachmentName;
                        att.parentId = acct.Id;
                    }
                    upsert att;
                    return att.Id;
                } else {
                    return 'Attachment Body was null';
                }
            } else {
                return 'Account could not be found';
            }
        } else {
            return 'Account Id was null';
        }
    }

    @RemoteAction
    public static String doSubmitForm(String acctId, String description, 
      String website, String phone) {
        if(acctId != null) {
            Account acct = getAccount(acctId);
            if(acct != null) {
                acct.Description = description;
                acct.Website = website;
                acct.Phone = phone;
                update acct;
                return 'Success';
            } else {
                return 'Account could not be found';
            }
        } else {
            return 'Account Id was null';
        }
    }

    private static Account getAccount(String acctId) {
        list<Account> accounts = [SELECT Id, Name, Counter__c
                                  FROM Account
                                  WHERE Id =: acctId];
        if(accounts.isEmpty()) {
            return null;
        } else {
            return accounts[0];
        }
    }

    private static Attachment getAttachment(String attId) {
        list<Attachment> attachments = [SELECT Id, Body
                                        FROM Attachment 
                                        WHERE Id =: attId];
        if(attachments.isEmpty()) {
            Attachment a = new Attachment();
            return a;
        } else {
            return attachments[0];
        }
    }
}

At a high level, here’s what’s happening:

1. Select a file and click “Upload File”
2. The file is read as a binary string
3. The file is Base64 Encoded and some initial values are set
4. The file is sent to the controller in chunks that are small enough to fit in 
   a remoting call until the entire file has been uploaded.

If you don’t chunk the upload, you will receive an exception that states, 
“Input too long. [0, 1000000]”.  Maximum String length in apex of 6 million 
characters. You have to artificially reduce the maximum file size or you will 
end up with an error late in your upload of “String length exceeds maximum: 
6000000”.
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License