Salesforce – Drag and Drop File Uploader Component with Progress Bar – HTML5 and Pure Javascript Based

HTML5 Drag And Drop File
HTML5 based Drag And Drop File

You may find many ways to upload attachments in Salesforce using visualforce however most of them uses some Javascript libraries (means either you need to depend on static resources or add CDN in remote site settings) or they do not have progress bar or they are not drag and drop. I thought to create one simple Visualforce component which can show progress bar and have drag and drop capabilities.

Initially i gave though to use visualforce remoting however challenge was how to show progress bar and same challenge was with custom controller or extension also. So, i decided to create one simple Apex REST API to upload attachment.

@RestResource(urlMapping='/DragAndDrop/v1/*')
global with sharing class DragAndDropRESTAPI
{
    @HttpPost
    global static String attachDoc(){
        RestRequest req = RestContext.request;
        RestResponse res = Restcontext.response;

        String fName = req.params.get('FileName');
        String parId = req.params.get('parId');
        Blob postContent = req.requestBody; 

        Attachment a = new Attachment (ParentId = parId,
                                       Body = postContent,
                                       Name = fName);

        insert a;
        return a.Id;
   }
}

After this class, I created below Visualforce component which needs only two parameter – Parent record id where attachment needs to be loaded and height of component.

<apex:component >
         <apex:attribute name="parentId" type="String" description="Parent record where attachment would be attached"/>
         <apex:attribute name="DragDropHeight" type="String" description="Height in Pixel for Drag and Drop Section"/>
    <style>
        #holder {
            border: 2px dashed #ccc;
            width: 98%;
            height:95%;
            background-color:#ccc;
            -webkit-border-radius: 8px 8px 8px 8px;
            border-radius: 8px 8px 8px 8px;
            text-align: center;
            }
        #holder span, #uploadCompleted span {
            position: relative;
            top: 30%;
            transform: translateY(-50%);
            text-shadow: 2px 2px 2px #525252;
            font-size:3em;
            color:#A3A3A3;
            }
        #holder.hover { border: 2px dashed #636363; }

        #uploadStatus span{
            text-shadow: 2px 2px 2px #525252;
            font-size:1em;
             }

        #holder p { margin: 10px; font-size: 14px; }
        progress { width: 100%;  height:2em;  }
        progress:after { content: '%'; }
        .fail { background: #c00; padding: 2px; color: #fff; }
        .hidden { display: none;}
        .dragDropComponentSize{ height:{!DragDropHeight} ; overflow-y:auto; }
        body{width:98%;font-family: Helvetica,"Helvetica Neue",Arial,sans-serif;}
    </style>

<article class="dragDropComponentSize" id="dndContainer">
    <div id="holder" >
        <span id="holder_txt1"> Drag and Drop file here </span>
         <span  id="holder_txt2" class="hidden"> Upload </span>
    </div>
    <p id="upload" class="hidden"><label>Drag &amp; drop not supported by your browser, but you can still upload via this input field:<br /><input type="file" /></label></p>
    <p id="filereader">File API &amp; FileReader API not supported</p>
    <p id="formdata">XHR2's FormData is not supported</p>
    <p id="progress">XHR2's upload progress isn't supported</p>
    <p id="uploadStatus" class="hidden"><span>Upload progress:</span> <progress id="uploadprogress" min="0" max="100" value="0">0</progress></p>
</article>
<script>
    var holder = document.getElementById('holder');
    var holder_txt1 = document.getElementById('holder_txt1');
    var holder_txt2 = document.getElementById('holder_txt2');
    var uploadStatus = document.getElementById('uploadStatus'); 

    var sfdcHostName =window.location.host.split('.')[1]; 

    tests = {
      filereader: typeof FileReader != 'undefined',
      dnd: 'draggable' in document.createElement('span'),
      formdata: !!window.FormData,
      progress: "upload" in new XMLHttpRequest
    },
    support = {
      filereader: document.getElementById('filereader'),
      formdata: document.getElementById('formdata'),
      progress: document.getElementById('progress')
    },
    progress = document.getElementById('uploadprogress'),
    fileupload = document.getElementById('upload');

"filereader formdata progress".split(' ').forEach(function (api) {
  if (tests[api] === false) {
    support[api].className = 'fail';
  } else {
    support[api].className = 'hidden';
  }
});

function textBeforeDrag(flag){
    if(flag)
    {
        holder_txt1.className = '';
        holder_txt2.className = 'hidden';
    }else{
        holder_txt1.className = 'hidden';
        holder_txt2.className = '';
    }
}

function resetAll()
{
    holder.className = holder_txt1.className = '';
    holder_txt2.className = uploadStatus.className = 'hidden';
}

function readfiles(files) { 

    var formData = tests.formdata ? new FormData() : null;

      //Not sure why multiple files dropping, so for time being disable multi file functionality
      if(files.length > 1)
      {
          alert('Multi Upload is not supported, please try to upload single file');
          return;
      }

    for (var i = 0; i < files.length; i++) {
        uploadStatus.className = '';
        holder.className = 'hidden';
        // now post a new XHR request
        if (tests.formdata) {
          var xhr = new XMLHttpRequest();

          var sfdcurl = 'https://'+sfdcHostName+'.salesforce.com/services/apexrest/DragAndDrop/ v1?FileName='+encodeURIComponent(files[i].name)+'&cType= '+encodeURIComponent(files[i].type)+ '&parId={!parentId}' ;

          xhr.open('POST','/services/proxy' );

          //xhr.setRequestHeader("Content-type",'multipart/form-data');
          //xhr.setRequestHeader("Content-type",'');
          xhr.setRequestHeader("Authorization","Bearer {!$Api.Session_ID}");
          xhr.setRequestHeader('SalesforceProxy-Endpoint', sfdcurl);
          xhr.setRequestHeader('X-User-Agent', 'DragAndDropAPI v1.0');

          xhr.onload = function() {
            progress.value = progress.innerHTML = 100;
            //resetAll();
          };

          if (tests.progress) {
            xhr.upload.onprogress = function (event) {
              if (event.lengthComputable) {
                var complete = (event.loaded / event.total * 100 | 0);
                progress.value = progress.innerHTML = complete;
              }
            }
          }

          xhr.onreadystatechange=function()
            {
                if (xhr.readyState==4 && xhr.status != 200)
                {
                    if(xhr.responseText)
                        alert(xhr.responseText);
                    else
                        alert('Some error occurred while uploading file');

                     console.log(xhr);
                }
            }

          xhr.send(files[i]);
        }
    } 

    /*
    if("{!isRefresh}".toUpperCase()  == "TRUE")
    {
        location.reload();
    }
    */
}

if (tests.dnd) {
  holder.ondragover = function () {
        this.className = 'hover';
        textBeforeDrag(false);
        return false;
      };
  holder.ondragend = function () {
        this.className = '';
        textBeforeDrag(true);
        return false;
      };
  holder.ondrop = function (e) {
        textBeforeDrag(true);
        this.className = '';

        e.preventDefault();
        readfiles(e.dataTransfer.files);
  }
} else {
        fileupload.className = 'hidden';
        fileupload.querySelector('input').onchange = function () {
        readfiles(this.files);
  };
}

</script>
</apex:component>

And we are now good to use above component anywhere in our salesforce application. For example, I used this component in one Visualforce page and added it as inline Visualforce in my Account Page layout.

<apex:page standardController="Account" docType="html-5.0" >
    <c:DragDrop parentId="{!$CurrentPage.parameters.id}" DragDropHeight="100px" />
</apex:page>

Demo is shown in below youtube video.

Note : Please make sure you are setting up doctype as html5 in your visualforce page.
This code is also available on my github account.


Posted

in

, ,

by

Comments

59 responses to “Salesforce – Drag and Drop File Uploader Component with Progress Bar – HTML5 and Pure Javascript Based”

  1. Bujji Avatar
    Bujji

    Hi,

    I tried the above, but i am seeing the below errors under the Drag and Drop Image in the inline vf page.

    File API & FileReader API not supported

    XHR2’s FormData is not supported

    XHR2’s upload progress isn’t supported

    Thanks,
    Bujji

    1. Kirtesh Jain Avatar
      Kirtesh Jain

      I found that Safari and IE8/IE9 doesn’t support File API/ File Reader API . which browser did you use ?

      1. Jitendra Zaa Avatar

        I used chrome and IE 11

        1. Krishan Gopal Avatar
          Krishan Gopal

          Hi Jitendra,
          Unknown property ‘isRefresh’ error when try to save visualforce component

          if(“{!isRefresh}”.toUpperCase() == “TRUE”)
          {
          location.reload();
          }

          when I remove this code snippet in visulforce compenent it will not shown uploaded file at record without refreshing web page.

          And one more issue I face is progress war not shown % upload in progress war shown only % sign. value should be shown in numeric how much data is uploaded.

          At end this code is not working in Internet Explorer, showing error msg “XHR2s upload progress is not supported”.

  2. juan Avatar
    juan

    Thanks the code is awesome, but I;m having some issues the responseText comes empty. Any ideas or why?

  3. Juan Avatar
    Juan

    Hi I tried the above and works great, thansk you saved me a lot of work I had no idea of how to do it, but not for IE11, so I found a workaround for this Instead of sending the file I chek if it is IE or not. And if it is I use a FormData to send the file

    if ((navigator.userAgent.indexOf(“MSIE”) != -1 ) || (!!document.documentMode == true ))
    var formData = new FormData();
    formData.append(“myfile”, fileVar);
    xhr.send(formData);
    }
    else
    {
    xhr.send(fileVar);
    }

    Hope it helps someone

    1. Jitendra Zaa Avatar

      Thanks juan, i will update this code once i get time.

    2. Don Mowen Avatar
      Don Mowen

      Thank you. That was pretty great. Took me a minute to get the whole page to refresh and not just the embedded visualforce page. Here’s what I got to work:

      xhr.addEventListener(“load”, transferComplete);

      window.top.location=’/{!parentId}’;
      function transferComplete(evt) {window.top.location.reload(true);}

  4. Chidanand Avatar
    Chidanand

    @JitendraZaa:disqus Bro this code Doesn’t allow me to drop multiple files. Any workaround?

    1. Jitendra Zaa Avatar

      Hey Chidanand, it currently doesnt support multiple files, I will try to tweak code and update this article. meantime if you are able to do so, please share 🙂

      1. Priyanka Singh Avatar
        Priyanka Singh

        Hello Zaa sir,I am new to Rest API,I tried this code bt getting error..plz check

        1. Sameer Jaffery Avatar
          Sameer Jaffery

          Hi Jitendra,
          Thanks for the great share been looking for this kind of functionality for long time.

          But I am also facing the same issue as above. Please can you point out where I am going wrong.

          Thanks
          Sameer

        2. Dhairya Avatar
          Dhairya

          The problem of getting this error might be you are trying to use in developer org which has managed package so you just need to add /package-name/DragAndDrop/v1*

          It will work!
          Thanks

          1. Harish Avatar
            Harish

            Hi Dhariya,

            I’m using test sandbox. Where I ll get the package of DragAndDrop – I am newbie to REST API so, bit confused.

      2. force.comlearner Avatar
        force.comlearner

        Jitendra Zaa have you implemented for multiple files drag n grop. can you please share it here…

        thanq

        1. Aamir Avatar
          Aamir

          Removing below code worked for me.. I am not sure why should that be difficult after such a great implementation..

          //if (files.length > 1) {
          // alert(‘Multi Upload is not supported, please try to upload single file’);
          // return;
          //}

    2. Priyanka Singh Avatar
      Priyanka Singh

      Hi i tried this code…but getting below error..please help

  5. chethan Avatar
    chethan

    hey
    i tried the code, following error appears after the file has uploaded completely. not able to upload any file, please help

    Error 400 Unable to forward request due to: Invalid uri ‘https://ap2.salesforce.com/services/apexrest/DragAndDrop/ v1?FileName=forcecom_workbook_VF_15.pdf&cType= application%2Fpdf&parId=00128000004P4z6’: escaped absolute path not valid

    HTTP ERROR 400
    Problem accessing /services/proxy. Reason:
    Unable to forward request due to: Invalid uri ‘https://ap2.salesforce.com/services/apexrest/DragAndDrop/ v1?FileName=forcecom_workbook_VF_15.pdf&cType= application%2Fpdf&parId=00128000004P4z6’: escaped absolute path not validPowered by Jetty://

    1. Jitendra Zaa Avatar

      There is space in your URL before “v1” , It should work if URL is correct. – https://ap2.salesforce.com/services/apexrest/DragAndDrop/ v1

      1. chethan Avatar
        chethan

        Hey, I made that change but still it’s not working.. I removed space in component. Is there any other place I should change.

        1. kamendra singh Avatar
          kamendra singh

          Hi Chetan,

          I am aslo getting same error,when i am trying to drag and drop file.did you you resolve that problem.please let me know.

          Thanks,
          Kamendra Singh

  6. Sameer Avatar
    Sameer

    I am facing this problem..

    plz solve this….

    Thanks

    Sameer

    URL No Longer Exists
    You have attempted to reach a URL that no longer exists on salesforce.com.

    You may have reached this page after clicking on a direct link into the application. This direct link might be:

    • A bookmark to a particular page, such as a report or view

    • A link to a particular page in the Custom Links section of your Home Tab, or a Custom Link

    • A link to a particular page in your email templates

    If you reached this page through a bookmark, you are probably trying to
    access something that has moved. Please update your bookmark.

    If you reached this page through any of the other direct links
    listed above, please notify your administrator to update the link.

    If you reached this page through a link on our site, please report the broken link directly to our Support Team
    and we will fix it promptly. Please indicate the page you were on when
    you clicked the link as well as any other related information. We
    apologize for the inconvenience.

    Thank you again for your patience and assistance. And thanks for using salesforce.com!

    1. Jitendra Zaa Avatar

      When you are receiving this error ?

      1. Sameer Avatar
        Sameer

        Problem solved…..thanks to reply me……

  7. Rida Jamal Avatar
    Rida Jamal

    XHR2s upload progress is not supported i have this error while trying to upload from IE 10 ? any idea please ?

  8. kamendra singh Avatar
    kamendra singh

    Hi Jitendra,

    I tried many that code but it’s not working,getting error.Please help me for same

    Thanks,
    Kamendra Singh

    1. Jitendra Zaa Avatar

      What error you are getting ?

      1. kamendra singh Avatar
        kamendra singh

        Hi Jitendra,

        I am getting below error.please help me for same-

        1. kamendra singh Avatar
          kamendra singh

          Hi Jitendra,

          Did you get chance to look that error.

          Thanks,
          Kamendra Singh

          1. Bee Kay Avatar
            Bee Kay

            Please see post above. It should help.

  9. Anvesh Avatar
    Anvesh

    Hello Jitendra,
    We are trying this code but we are getting 400 bad request. Can you please help me?

    1. Bee Kay Avatar
      Bee Kay

      Please see my post above. It should help.

  10. Bee Kay Avatar
    Bee Kay

    Wanted to give back something 🙂

    1. For all those getting “Error 400 Unable to forward request due to: Invalid uri” error, please remove TWO spaces from the below statement in the code, one after DragAndDrop/ and second in ‘&cType= ‘, like so: var sfdcurl = ‘https://’+sfdcHostName+’.salesforce.com/services/apexrest/DragAndDrop/v1?FileName=’+encodeURIComponent(files[i].name)+’&cType=’+encodeURIComponent(files[i].type)+ ‘&parId={!parentId}’ ;

    2. I noticed that this code doesn’t refresh the page after an upload, so the user can’t see what they attached right away. Here is the snippet that you could add to the component that auto-refreshes the page after a successful transfer:


    xhr.setRequestHeader(“Authorization”,”Bearer {!$Api.Session_ID}”);
    xhr.setRequestHeader(‘SalesforceProxy-Endpoint’, sfdcurl);
    xhr.setRequestHeader(‘X-User-Agent’, ‘DragAndDropAPI v1.0’);

    xhr.addEventListener(“load”, transferComplete);
    xhr.addEventListener(“error”, transferFailed);
    xhr.addEventListener(“abort”, transferCanceled);

    function transferComplete(evt) {
    console.log(“————————–> The transfer is complete.”);
    window.location.reload();
    }

    function transferFailed(evt) {
    console.log(“————————–> An error occurred while transferring the file.”);
    }

    function transferCanceled(evt) {
    console.log(“————————–> The transfer has been canceled by the user.”);
    }

    3. Test class. See below:

    @isTest
    private class DragAndDropRESTAPI_Test{

    static testMethod void drag_drop_attachment_tester(){

    Account acc = new Account(Name = ‘TEST ACCOUNT’);
    insert acc;

    RestRequest req = new RestRequest();
    RestResponse res = new RestResponse();

    req.requestURI = ‘/DragAndDrop/v1/*’;
    req.addParameter(‘FileName’, ‘TestFileName’);
    req.addParameter(‘parId’, acc.Id);
    req.requestBody = Blob.valueOf(‘TEST CONTENT’);

    req.httpMethod = ‘GET’;
    RestContext.request = req;
    RestContext.response = res;

    Id newly_created_attachment_id = DragAndDropRESTAPI.attachDoc();
    }

    }

    1. satish Apple Avatar
      satish Apple

      hi bee kay.. Am still getting that error. would you mind please help me

    2. pavan Avatar
      pavan

      Failed to load resource: the server responded with a status of 404 (Not Found)

    3. Zachary Alexander Avatar
      Zachary Alexander

      You are a wonderful person. I kneel down and kiss your feet. Thank you!

    4. Don Mowen Avatar
      Don Mowen

      Wow. Thank you Jitendra and Bee Kay. That was pretty great. Took me a minute to get the whole page to refresh and not just the embedded visualforce page. Here’s what I got to work:

      xhr.addEventListener(“load”, transferComplete);

      window.top.location=’/{!parentId}’;
      function transferComplete(evt) {window.top.location.reload(true);}

  11. Dashing hunk Avatar
    Dashing hunk

    Fails in Sites when a VF page is setup so there is no login required.

    Error message “INVALID_SESSION_IDSession expired or invalid”

  12. Ido Greenbaum Avatar
    Ido Greenbaum

    Thank you for posting this useful Drag n’ Drop solution.

    I successfully implemented and tweaked this in my Sandbox environment, and having difficulties pushing this to Production.
    Perhaps you can share the Tests you created for this please?

    Thank you.

  13. Cristiano Sinadino Avatar
    Cristiano Sinadino

    Thanks Jitranda for the amazing tutorial! I am wondering if anybody could comment on this error: Stream Closed. I get this error every time I drop an attachment in the box. Thank you in advance for taking the time to read it.

    1. Cristiano Sinadino Avatar
      Cristiano Sinadino

      I found the solution to the error.
      var sfdcHostName =window.location.host.split(‘.’)[1]; was not parsing the URL properly.

      1. Anshuman Chauhan Avatar
        Anshuman Chauhan

        Could you please paste your code for parsing the URL as we are facing same error

        Thanks!

  14. Navaneeth Arya Avatar
    Navaneeth Arya

    is there any possibility to upload folders?

  15. Shahid Avatar
    Shahid

    I am facing CORS issue when I am trying to use this code from a community portal because of the REST API call. is there any workaround?

  16. Rochelle Schlaud Avatar

    I’m having an issue implementing this on a Site with a Guest User Profile, it works great as a logged in user.
    I confirmed the Guest User has premissions on the parent object. Any suggestions?

  17. Hrushikesh Avatar
    Hrushikesh

    Hello Jitendra,

    The code is working for normal salesforce users. I want to use this for partner users but it does not work.

    I guess because of iFrame the event.datatransfer.files is probably not getting the value but thats my guess. I tried with ‘event.originalEvent.datatransfer…..’ but it didn’t work.

    Any pointers you would like to suggest?

    Thanks,
    Hrushikesh

    1. Hrushikesh Avatar
      Hrushikesh

      I found the answer for this.The functionality started running for portal users after APIs were enabled for them.

  18. Suresh Avatar
    Suresh

    Hi jitendra after removing the space also i a
    will getting the error like “couldn’t find a.match for URL/DragAndDrop/V1”.

    Can u please help me in this..

  19. Sanjeev Avatar
    Sanjeev

    Hi jitendra can we do this in lightening ?

  20. Pavithra Senthil kumar Avatar
    Pavithra Senthil kumar

    Hi , we are trying to use this code , but we get this error in IE11

    XHR2’s upload progress isn’t supported

    Let us know what is the fix for this.

    1. Subba Salesforce Avatar
      Subba Salesforce

      Hi Pavithra ,I am also facing the same problem .Please let us know if you did this fix
      Advanced Thanks

  21. Steve Fouracre Avatar
    Steve Fouracre

    Im using Google Chrome and Im Sys Admin and I get these errors

    File API & FileReader API not supported
    XHR2’s FormData is not supported
    XHR2’s upload progress isn’t supported

    Im assuming this doesnt work in all browsers?

  22. Subba Salesforce Avatar
    Subba Salesforce

    Hi Jitendra,
    1.Which is supporting for only single file upload. Is there any option for multi-files upload ?
    2 Is there any option,i.e.,adding the description during file uploading like Bank Statement,Document etc..?

  23. Subba Salesforce Avatar
    Subba Salesforce

    Hi Jitendra,
    1.Which is supporting for only single file upload. Is there any option for multi-files upload ?
    2 Is there any option,i.e.,adding the description during file uploading like Bank Statement,Document etc..?

  24. Subba Salesforce Avatar
    Subba Salesforce

    Hi Jitendra the code is not supporting in Internet Explorer Can you please post the solution
    Thanks

  25. Subba Salesforce Avatar
    Subba Salesforce

    Can you Please suggest me how to refresh automatically attachment section after uploading the files?

  26. Manisha Sadhnani Avatar
    Manisha Sadhnani

    Hello, If anyone facing issue for ‘Session expired or invalid’ then replace ‘var sfdcurl = ‘https://’+sfdcHostName+’.salesforce.com/services/apexrest/DragAndDrop/ v1?FileName=’+encodeURIComponent(files[i].name)+’&cType= ‘+encodeURIComponent(files[i].type)+ ‘&parId={!parentId}’ ;’ with ‘var sfdcurl = ‘https://’+sfdcHostName+’.salesforce.com/services/apexrest/DragAndDrop/v1?FileName=’+encodeURIComponent(files[i].name)+’&cType=’+encodeURIComponent(files[i].type)+ ‘&parId={!parentId}’ ;’. URI must not contain space.

  27. Tony Avatar

    Hello, this looks promising but I am not able to get it to render on an account detail page. Is anyone still using this?

  28. Deba Sen Avatar
    Deba Sen

    Hi Jitendra,

    Awesome post. Works perfectly for a logged in user. But for a guest user on a force.com site, this returns a 503 error. Any idea how to solve this?

Leave a Reply to Navaneeth Arya Cancel reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.