
Posted on • Originally published atraymondcamden.com on
Uploading Multiple Files with Fetch
This afternoon I was going through my "blog ideas" list and cleaning up entries I've changed my mind on. I came across something I added many months ago - using theFetch API to upload multiple files at once. The reason it's stuck in my "todo" pile for so long is that I wasn't aware of a good service I could use to post my files against. I've done it before in Node.js and I know it's something I could do locally in an hour, but honestly I just didn't want to. That probably sounds a bit lazy but it's honest. Today though I came acrosshttpbin.org, an online service that lets you hit it with various types of HTTP methods and even supports file uploads. (Obviously it doesn't make those files available, it just reports back on the upload.) Even better, it supports CORS which means I could use CodePen. So with no more excuses at my disposal, today I finally built a simple demo.
First off, I created a simple form:
<form><inputid="filesToUpload"type="file"multiple><buttonid="testUpload">Test Upload</button></form><divid="status"></div>
I've got a file field, a button, and an empty div. Notice the file field uses themultiple
attribute. This lets the end user select one or more files. For my first iteration, I used the following #"http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24">
From top to bottom - I begin by usingquerySelector
to cache access to my file field and empty div. Then I add a click handler to the button.
The click handler first checks to see if any files were selected. If none were then we print out a message and leave. Otherwise, we then iterate over thefiles
array and call an async function,uploadFile
. In my demo,uploadFile
does aPOST
to httpbin and returns the result. Right now I'm ignoring the result but in a real application you would probably need something from there. At the end of each upload I update my div with a status.
Finally I report that everything is complete and reset the file field. Here's a CodePen for you to try it out yourself:
This works well, but uploads the files one after the other. It would be nicer if they were all uploaded at once, right? Here's an updated version that does that:
document.addEventListener('DOMContentLoaded',init,false);letfileField,statusDiv;asyncfunctioninit(){fileField=document.querySelector('#filesToUpload');statusDiv=document.querySelector('#status');document.querySelector('#testUpload').addEventListener('click',doUpload,false);}asyncfunctiondoUpload(e){e.preventDefault();statusDiv.innerHTML='';lettotalFilesToUpload=fileField.files.length;//nothing was selectedif(totalFilesToUpload===0){statusDiv.innerHTML='Please select one or more files.';return;}statusDiv.innerHTML=`Uploading${totalFilesToUpload} files.`;letuploads=[];for(leti=0;i<totalFilesToUpload;i++){uploads.push(uploadFile(fileField.files[i]));}awaitPromise.all(uploads);statusDiv.innerHTML='All complete.';fileField.value='';}asyncfunctionuploadFile(f){console.log(`Starting with${f.name}`);letform=newFormData();form.append('file',f);letresp=awaitfetch('https://httpbin.org/post',{method:'POST',body:form});letdata=awaitresp.json();console.log(`Done with${f.name}`);returndata;}
The main difference is tht now I don'tawait
the call touploadFile
and use the implied Promise returned instead. I can then usePromise.all
on the array of uploads to notice when they are all done. One thing I don't have is the nice "X of Y" message, and that's possibly something I could do too, but for now the improved speed should be nice. If you want to test this version, it's below.
Enjoy, let me know what you think!