65
votes

I want to upload a (single) file to a server and show the progress of the upload.

I know I can upload a file using HTTP POST. I'm not familiar with web-sockets, but as I understand, binary data can also be sent that way and because web sockets are bi-directional I could get the progress of the upload.

I'm not worried about older browsers so iframe's and flash solutions aren't very appealing unless there is a significant advantage in going that route.

I'm also curious as to the best server-side technology. Are their advantages to using a WSGI server like Django? Or maybe non-blocking I/O technology like Node.js? I'm not asking if web framework x is better than web framework y, or server x is better than server y. But simply what the ideal technology should have in order to facility uploads in the client.

Update: It seems like the server side does not have bearing on the technologies/API's available on the client to facilitate uploads.

5
To the person who voted to close my question, why is it off-topic? I have a specific programming problem, that is answerable... Many other people have asked this question, but as browser technology improves the answers for this problem may change.Tom
vtortola's answer is the best way to handle what your asking...use the HTML5 File API. Server side is up to you. Whatever platform best suits your overall needs. There's not going to be any advantage to using any one platform over another for just uploading a file. (I'm not the person who voted off-topic, just felt like this is more of a comment than an answer)DM Graves
I removed the bit about server-side technologies. As that could be slightly opinion based and does not seem to effect the client side options to facilitate uploads.Tom
Here is an answer to similar question I did sometime in the past: stackoverflow.com/questions/28014386/…lehins
There should be nothing wrong with having a question and answer like this on Stack Overflow. Unless Stack Overflow is not trying to be as useful as it can be. A developer can enjoy this question and answer very much, and it is extremely handy to read about. What is the alternative for someone when they have such a question? Why should an answer to this not be available on Stack? Seems very strange.Yehuda Makarov

5 Answers

64
votes

Edit (2017-10-17): As of now, there is also the option to use Fetch API. It offers essentially the same capabilities as XMLHttpRequest behind a more modern promise-based API. There is a polyfill for browsers that don't support window.fetch() natively (which is mainly Internet Explorer and older Safari versions right now).

XMLHttpRequest vs. Web sockets vs. Something else

Clearly XMLHttpRequest. Its capabilities in modern browsers are enormous and cover almost all scenarios. It will produce a standard POST or PUT request, any web server and framework combination can deal with that.

While web sockets are nice for some scenarios, it's a different protocol that adds lots of complexity - they are only worth using if you need real-time responses from the server. And as you noted yourself, other approaches like Flash are merely ugly hacks.

Sending binary data

Normally, you won't have direct access to files. So you will have an <input type="file"> form field somewhere on your page and wait for the user to choose a file. The options then are:

  • Sending only the file contents: request.send(input.files[0]). The request body will be the file's contents and nothing else, no encoding will be performed and no metadata like file name will be transmitted. Browser compatibility: Chrome 7, Firefox 3.6, Opera 12, IE 10.
  • Sending the data of the entire form: request.send(new FormData(input.form)). Here the form contents will be encoded as multipart/form-data, meaning that you can send multiple form fields and metadata like field and file names will be transmitted as well. You can also modify the FormData object before sending it. Depending on the server-side framework, handling this request might be simpler than raw data, there are typically many helpers you can use. Browser compatibility: Chrome 6, Firefox 4, Opera 12, IE 10.
  • Sending a typed array: just in case you don't have a file but merely want to send some binary data you generate on the fly. No extra encoding is being performed here, so as far as the server side is concerned this works like sending file contents. Browser compatibility: Chrome 9, Firefox 9, Opera 11.60, IE 10.

Displaying upload progress

You can listen to progress events on XMLHttpRequest.upload. The progress events have loaded and total properties that allow determining how far you've got with your request. Browser compatibility: Chrome 7, Firefox 3.5, Opera 11.60, IE 10.

JavaScript libraries

There are of course existing libraries wrapping the functionality outlined here. These are mentioned in other answers, searching on the web will certainly turn up even more. I explicitly don't want to propose any libraries here - which of them if any you should use is purely a matter of preference.

21
votes

My answer is quite late but here it goes:


Short answer:

XMLHttpRequest is the best way to upload files in a modern browser.



What is XMLHttpRequest?

XMLHttpRequest is a JavaScript object that was designed by Microsoft and adopted by Mozilla, Apple, and Google. It's now being standardized in the W3C. It provides an easy way to retrieve data from a URL without having to do a full page refresh. A Web page can update just a part of the page without disrupting what the user is doing. XMLHttpRequest is used heavily in AJAX programming.

Despite its name, XMLHttpRequest can be used to retrieve any type of data, not just XML, and it supports protocols other than HTTP (including file and ftp).

The XMLHttpRequest object has gotten a facelift in the Html5 specifications. Specifically the XMLHttpRequest Level 2.


Advantages:

  • Handling of byte streams such as File, Blob and FormData objects for uploading and downloading
  • Progress events during uploading and downloading
  • Cross-origin requests
  • Allow making anonymous request - that is not send HTTP Referer
  • The ability to set a Timeout for the Request
  • Uploading is happening in the background
  • The page the user is on remains intact
  • Does not require any change to the server side, so existing server side logic should remain unchanged, which makes adapting this technology that much easier.

The Html5 Progress Event:

As per the Html5 Progress Events spec, the Html5 progress event provides, among others, the following information :

total - Total bytes being transferred
loaded - Bytes uploaded thus far
lengthComputable - Specifies if the total size of the data/file being uploaded is known 

Using the information above, it is fairly easy to provide the "Time remaining" information to the user.


Keep the user informed:

Information about the file that can be made available to the user:

  1. File Name
  2. File Size
  3. Mime Type
  4. A Progress bar with percent complete
  5. The upload speed or upload bandwidth
  6. The approximate time remaining
  7. The bytes uploaded thus far
  8. A response from the server side

File Upload using XMLHttpRequest Demo

Please check "Uploading files using Html5 with Progress indication demo" for an example. All of the JavaScript code required is in the page but no CSS is included. For security reasons, the file types are limited to jpg, png, gif and txt. Max file size is 2MB.


XMLHttpRequest Browser Compatibility:

XMLHttpRequest Browser compatibility


5
votes

Probably the Javascript's file API is the best way in modern browsers:

http://robertnyman.com/2010/12/16/utilizing-the-html5-file-api-to-choose-upload-preview-and-see-progress-for-multiple-files/

http://www.sitepoint.com/html5-javascript-file-upload-progress-bar/

Server side wise... I think any of the main frameworks has the HTTP file POST feature well covered.

4
votes

Files can be uploaded via AJAX.

Use the jQuery form plugin. It does all the dirty work of binding the files to the form and serializing it. It is also capable of showing upload progress.

Server stack hasn't got much to do with it.

Demo

4
votes

I personally like blueimp jQuery File Upload Plugin (https://blueimp.github.io/jQuery-File-Upload/)

File Upload widget with multiple file selection, drag&drop support, progress bars, validation and preview images, audio and video for jQuery. Supports cross-domain, chunked and resumable file uploads and client-side image resizing. Works with any server-side platform (PHP, Python, Ruby on Rails, Java, Node.js, Go etc.) that supports standard HTML form file uploads.

Demos:

Download (GitHub): https://github.com/blueimp/jQuery-File-Upload