File Upload Lightning Component

The Lightning Component Framework was introduced at Dreamforce ’14 as part of the collection of tools that represent a significant upgrade to the Salesforce1 Platform.  Currently the components can be used in the Salesforce1 mobile app and standalone apps.  In this article I detail a custom Lightning Component that allows a user to upload a file to Salesforce as a related Attachment record on a specified sObject record.  The article starts off by showing a simple component that works for smaller files (< 1MB) and then builds on that with a component that works on larger files (~4.5 MB).

Component Summary

The component consists of the following:

  • fileUpload.cmp – The component definition – Basic markup and attribute definitions
  • fileUploadController.js – The controller – Hands off processing to helper
  • fileUploadHelper.js – The helper – Sets up call to Apex controller and handles result
  • fileUpload.css – The styles – Basic styles related to the spinner for uploading
  • FileController.cls – The ApexController – Creates the Attachment

The code of fileUpload.cmp, fileUploadHelper.js, and FileController.cls are shown in this article; however, all of the code is available on GitHub.

The component has some very, very basic styling since that is not the focus of this article.  It first displays with a button to select a file and a save button. While saving it displays the spinner.

upload selection
Select a file
file uploading
File uploading

Component Code

The component markup is as follows:

The component accepts the Id of the related object as an attribute, contains a file input, save button, and spinner that is displayed when the file is being uploaded.

Note the use of the “aura:id” on the file input.  Using “aura:id” is necessary to be able to use the component.find(“file”) method in the helper JavaScript to locate the file input.  What’s very powerful here is that the input element is itself translated into a component by the framework and gets all of the benefits of being a component along with it.

The aura:waiting and aura:doneWaiting are events defined in the component markup and are fired by the framework when the component has invoked an Action that calls to the server and when the response is received, respectively.  The controller actions that they invoke simply hide or show the spinner div by applying CSS.

Uploading

The controller’s save method is called when the save button is pressed.  It delegates to the helper’s save method to handle the work.

The helper’s save function gets a reference to the action that represents the Apex controller’s saveTheFile method.  Note that the name of the method (saveTheFile) on the Apex controller is different from the JavaScript controller (save).  If they were both named “save”, then the call to component.get(“c.save”) would evaluate to a reference to the JavaScript’s save action, that would get called and infinite loop would result.  Users are protected against the infinite loop and the result is actually an error overlay with a message “finishFiring has not completed after 15 loops”.

The save function reads the file using a FileReader and its readAsDataURL method.  The readAsDataUrl method reads the file contents into a blob containing a data: URL.  The maximum String size that can be sent to an @AuraEnabled Apex controller method appears to be 1,000,000, which is consistent with @Remote method limits.  The MAX_FILE_SIZE takes into account the size increase due to Base64 encoding.  It is necessary to URL encode the file contents prior to sending them to the server.  This is done with the encodeURIComponent function.

The Apex controller simply takes the input, saves it in a Attachment and returns an Attachment Id.  Since the data was URL encoded on the client the method decodes it with the EncodingUtil.urlDecode method.

Larger Files

The above is a great start, but is limited by the small file size.  Derek Lansing blogged, in this article, about a good way to handle larger files when uploading using @Remote methods that increases the allowed file size to about 4.3 MBs.  That approach reads the file, and then sends it to the @Remote method in chunks that are under the limits.  The Apex controller method then selects the Attachment out of the database on each successive iteration and updates its body.

Adapting the Lightning component to chunk the files, results in the following helper. Note that the component and the controller JS remain unchanged.

In this version the Attachment’s Id is an additional parameter that is sent to the Apex controller.  On the initial invocation the Attachment’s Id is not set.  It is returned by the Apex controller and then retrieved in the helper with the a.getReturnValue() method call.

On successive calls the Apex controller performs an update.  Below is the updated Apex controller.

More

In this article I detailed a simple Lightning component for uploading smaller files and expanded on it with a slightly more complex component to support larger files.  To learn more about Lightning see Salesforce1 Lightning Developer page for a list of resources.

All of the code is available on GitHub.

77 thoughts on “File Upload Lightning Component

  1. Great post. Have you found the upper limit of file size that can be uploaded using the second method? I’d expect heap space to be the gating factor myself, but curious as to your experience.

    1. Thank you! Glad you liked it.

      The upper bounds of the file size is related to the maximum String size of 6,000,000 characters which I think is related to the maximum heap size. Since the Apex controller concatenates Strings to build the body as soon as the base64 encoded concatenated body String exceeds 6,000,000 characters the following error is generated: FATAL_ERROR System.StringException: String length exceeds maximum: 6000000. The maximum heap size is 6MB, which is kind of equal to that. 😉

      The base64 encoding results in about a 33% increase in size (i.e., when the file is represented as a base64 string it is ~33% larger), so the upper bounds is about 3/4 * 6,000,000 = 4,500,000.

      1. Hi great post, I was using it as a guide for some development I need, but found that I reached the heap space limit, after 1MB of the file was sent. Did you had this kind of issue before? or any recommendatios I should keep in mind?

        Thanks

        1. Helt ofa8;bart&#t230t. man finner inga ord.NÃ¥gon sa idag ”is the man insane or a psycopath?” Ja, du det kan man frÃ¥ga sig….Tack för kommentar inne hos mig!kram

      2. Thanks peter for script.
        With same code, i have tried to insert image into ContentVersion but getting same error “Apex heap size too large: 16227892” … Is there any way to avoid heap size error?

    2. let me think on it. i think having all these is neat (but a bit weird). i know there is a neat way to display them you just need to find the right person who can tell you. ruatnnhe has good ideas.

  2. Nice post Pete.But When i tried to pass “parentId” as url parameter nothing happens ,instead of it used this way it works.Is there’s any way to pass parentid in component?

  3. Great Post. !

    regarding the maximum String size of 6,000,000 , in what any other way can we upload file greater than 4.3M ?

    I’m using javascript remoting, so in that case we are stuck…. hope for any guidance if you can

    Ronen

  4. This is great and was a ton of help. I couldn’t find anything else on uploading files with Lightning. Is there a way to clear the selected file once it has been saved? I created an input form and I clear all the fields once it has been saved but I don’t know how to clear the file.
    Thanks,
    Brian

    1. Took me a little time to figure it out, I was going to refresh the whole component at first, however, it is fairly simple:

      component.find(“file”).getElement().value=”;

      This should work, where “file” is aura:id of input.

  5. hi,
    Great post and clearly explained, thanks!
    I ‘m working on a similar component and i’m currently having a hard time to make it work within the salesforce1 native app on ios. Have you tested this component on the native app?
    thanks,
    andrea

  6. Hi Peter,
    Great code and thank you for sharing it.
    I have implemented your code and it works fine but I am running into more severe Heap size issue than what you are describing.
    This line:
    a.Body = EncodingUtil.base64Decode(existingBody + base64Data);
    seems to load the existing body twice in memory, once for a.Body and once for EncodingUtil.base64Decode(existingBody + base64Data) which makes the bigger file I can upload only around 1.5MB.

    Have you encountered the same issue?

  7. Hi Peter Knolle,

    I have got file too long error in above code can you please help me because i am working on a similar component but that time i have got lot of problem in upload large amount of file.

    Thanks

  8. Great article. How about the reverse though when you want to download files? Some blogs have reference to the /servlet/servlet URL. However with Lightning not sure if URL’s are constant and that doesn’t seem to be a supported approach. I’ve worked through a couple ideas (base64 encode href, etc). What have you done?

    1. Thanks.
      I hadn’t tried it. I just looked and the standard Notes & Attachment related list uses the servlet/servlet URL. As long as you put it in a new window, I’m pretty sure it would work, e.g., <a target="_blank" href="/servlet/servlet.FileDownload?file=00P36000000R3TlEAK">get attachment</a>;. I am not aware of any URLFOR like functionality that we could leverage, though.

  9. Hi Peter,

    I have tried your code for SF1 , file is not saving in proper format.Could you please provide me more options here?

    Thanks
    Raj

  10. Hello ,
    After trying this code i’m getting an error : Only functions are allowed in javascript controllers
    when trying to save the javascript controller. COuld you please Help.

  11. Hi,
    I am trying this component and I am continuously getting error “TypeError: Cannot read property ‘0’ of undefined” at following line:

    var file = fileInput.files[0];

    Does anybody have suggestion?

      1. I’d raise this on Stack Exchange – there’s no reason why a component that manages a file input shouldn’t have access via the Files API. It could just be that it hasn’t been added to the secure DOM side of things yet.

          1. Is there any indication that case 14030879 will be resolved before locker service is enabled for all orgs in Winter 17?

          2. Thanks Peter for this component.
            To Make it locker compatible –
            If we use and make other locker changes like replace “$A.run(function() {” with “$A.getcallback” then it works with locker service.
            To Access file input you can use –
            var fileInput = component.find(“currFile”).get(“v.files”);
            var file = fileInput[0];
            rest code remains same and works fine

  12. Hi Peter,

    This is very useful post for uploading small files. But i want upload a large video file. Can you please guide me how can achieve to upload large file. ?

  13. Hi Peter,

    File upload code is not working after summer 16 release. Can you please provide any solution for same?

    1. Free again???? I am trying to not let trash envy take over me. It looks amazing. I am loving that really rustic, chippy look. It really looks genuinely old, not like someone diseersstd it to make it look old. Great work and thanks for the fun party. -K

  14. Hi, Peter.
    It takes more long time for uploading the file when I use base64encode, so
    do you know some ways of using “URL.createObjectURL” instead of it.

    please help me.

  15. Hello Peter,

    Thanks for the article, nicely explained, have one quick question. FileReader and itsfuntion readAsDataURL are not working/supported when we activate ‘Enable Lightning LockerService Security for Communities’ critical update, this is going to get activated in Production on 2/11/2017 by default. Do you know any other hack or option to make file upload work with lightning components?

    Thanks & Regards,
    Ashok

  16. Hi Petar ,

    Since LockerService activation, $A.run is not supported anymore and triggers a JS error.
    SFDC doc says to replace it bu $A.enqueueAction , but unfortunately it enqueues the action but … doesn’t trigger it.

    Do you have an idea about some solution / workaround ?

    Many thanks

  17. Hi Peter ,

    Since LockerService activation, $A.run is not supported anymore and triggers a JS error.
    SFDC doc says to replace it bu $A.enqueueAction , but unfortunately it enqueues the action but … doesn’t trigger it.

    Do you have an idea about some solution / workaround ?

    Many thanks

  18. I implemented the same thing but it is taking too much of time to upload the file. Can you suggest me how can i reduce the upload time.

  19. i am facing wiered problem – Filereader.onload is getting executed faster, and hence., the file is not getting loaded during the first time click of the button. But when i click for the second time, there are 2 attachments added. Any idea how to fix it?

  20. Hi Peter,

    Thank you for the very helpful explanation. I implemented your solution and it works great for small files. However, when I try to load larger files (> 1 chunk) an error is thrown in the browser console: “Received exception event from server”. It looks like the issue has to do with the attachId value not being returned in the uploadChunk callback (or perhaps that is just a result of the error). I also checked the Force.com developer console and got an error: “FATAL_ERROR System.QueryException: List has no rows for assignment to SObject”, that I believe is related. Any ideas what may be going on?

    Thanks in advance for your help!

  21. Hi,

    Sorry if this is a dumb question, but I copied off Git Hub, but when trying to add it as a component in a lightening app, I get the following error

    “Action failed: c$fileUpload$controller$doneWaiting [component.find(…).getElement is not a function] Failing descriptor: {c$fileUpload$controller$doneWaiting}”

    Any ideas?

    1. I’m facing the same problem, but only if I’m using an init event in my fileUpload component.
      After removing following line:

      it works.
      But I have no idea why.

  22. HI Peter,
    You have mentioned that “component that works on larger files (~4.5 MB)” . Can we upload a file more than 4.5MB using lightning componet ?

  23. Hello Peter!
    Thank you! This article was very helpfull.
    But I am facing strange issue with this method. I am uploading really small files and save it as ContentDocument. Everything works fine but the callback runs after 1 to even 3 minutes. This is too long for such small files. Do you have an idea what could cause this issue. There is only one small trigger configuring contentVersion fields. anyway apex has 10 seconds limit so this is not the issue.

    Any suggestions will be greatful .
    Thanks

  24. http://lubelskiots.pl/warsztat-samochodowy-i-jego-oferta
    Purchasing a used or new car can be quite a difficult approach if you do not know what you really are performing. By educating yourself about car purchasing before you head to the dealer, you could make issues much easier yourself. The following tips might help your following store shopping vacation become more pleasant.

    Usually take a auto mechanic along when shopping for a fresh automobile. Automobile sellers are popular for marketing lemons and you do not need to be their up coming sufferer. Provided you can not have a auto technician to check out automobiles with you, no less than make sure that you have him take a look at final decision before you purchase it.

    Know your restrictions. Before you begin buying for your upcoming car or van, make a decision what you can afford to pay, and follow it. Don’t overlook to add curiosity about your calculations. You will definitely shell out around twenty percent as a payment in advance too, so be well prepared.

    Just before attending a dealership, know which kind of motor vehicle you want. Research most of you choices prior to purchasing so that you can decide what works for your budget and household needs. Do your homework to learn how much you ought to be paying for a possible car.

    Before signing any contract spend some time to read each and every collection, including the fine print. If there is something listed that you just do not fully grasp, usually do not indicator till you purchase an answer that you simply understand. Unsavory salesmen may use a contract to put in a lot of costs which were not mentioned.

    If you keep your preceding assistance under consideration next time that you just go looking for a car, you will end up prone to get a good bargain. Buying a vehicle lacks to be a headache. Simply use the guidelines out of this article and you will receive the automobile you need at a very good price.

  25. Hi Peter,

    We have a need to upload files over 25 MB to Filenet from Salesforce, could you please clarify if the above approach can be used? I am concerned that the heap size would be an issue although the outbound Rest API supports 2GB. Please suggest if there is an alternate way to accomplish the same.

  26. Hi Peter,

    Great stuff here. I was wondering if you had any thoughts about how to modify this for multiple attachments for the same record.

    Have you seen successful examples of this?

    Thanks!

  27. For those who’s having problem after the ‘$A.run’ deprecated. I suggest to use:

    $A.getCallback(function() {
    $A.enqueueAction(action);
    })();

    Thats works fine for me.

  28. Very nice! I am using this in a form. How could I force the user to add at least one attachment, making it required?

  29. Hi Peter,

    I have used your code of file upload for a larger size. but I am facing an issue. The issue is when I am trying to upload same file second time, it does not upload anything. However, if I upload a different file it uploads successfully. Please Let me know if you have any solution.

  30. Hi,

    I have used this code but somehow my files are getting attached twice. Can you please help? i am uploading a file of around 3mb pdf file.

    1. This lightning component was giving lot of issues while uploading pdf files. So i used vf page for file upload and embedded that in my lightning component. So now i can upload file upto 10mb.

      1. Hi Dharmil,

        We were doing same using withing the lightning component but as we are developing managed package so salesforce security has recommended us not to use iframe and suggested to use canvas app for it. So my my question is if you are also using iframe ? if not then could you please tell us some details around it.

        Thanks!

  31. Hi Peter,
    I have created a Lightning Component to create and send email. I would like to have the ability to attach file to the email. How do you advice me to use your component?
    Thanks in advance for your time.

  32. Hi Peter,

    I have implemented your code, it’s working fine for smaller file, but it’s not working for larger file. I am getting this error
    ‘Input too long. [1, 211]’. Can you please… please help me.

  33. Hi Peter,

    I am trying to upload 10 MB but it takes very long time. Any solution to reduce time.
    Can you please help me out.

    Thanks!

  34. Yes man.Glad you discovered and got this.Thank you so much piyush just now i saw your mail .Sorry for the delayed response man.cheers!!!!!

  35. Yes man.Glad you discovered and got this.Thank you so much piyush just now i saw your mail .Sorry for the delayed response man.cheers!!!!!!!!!!!!!

Leave a Reply

Your email address will not be published. Required fields are marked *