📜 ⬆️ ⬇️

HTML5 File API: multiple files upload to server

When I once again faced the task of simultaneously uploading several files to the server (without reloading the page, of course), I began to wander around the Internet in search of a rather clumsy jQuery plug-in that allows you to simulate ajax file download (the very same plugin that with a hidden frame: it was immediately decided to refuse java- and flash-plug-ins). During the search, I remembered that in the upcoming html 5 standard, the possibilities for working with files should be significantly expanded, and some of these possibilities are available now. As a result, it was decided to test them in action.

Consider the possibility of File API will be an example of the simultaneous upload of multiple images to the server. At the end of the article is a ready-made solution, designed in the form of jQuery-plugin.


I do not want to read this, I am interested in a ready-made solution.
')
So, what are the benefits of using the File API:
  1. Independence from external plugins
  2. Ability to control the download process and display information about it (the progress bar always adds patience to the user)
  3. The ability to read the file and find out its size before the download begins (in our example, this gives us the opportunity to weed out files that do not contain images and show thumbnails of pictures)
  4. Ability to select multiple files at once through the standard file selection field
  5. Ability to use the drag and drop interface to select files. Yes, yes, we can drag and drop files to download directly from the desktop or, for example, from the conductor!

Among the shortcomings can be noted only the lack of support in browsers. Currently, the File API only supports Firefox ≥ 3.6 and Chrome ≥ 6.0. There is a feeling that Safari will very soon be tightened, but nothing is clear about IE and Opera (maybe someone has information?). Upset of course, that the File API does not support IE9 Beta: this is strange, given that the IE developers are now heading for abundant html 5 support. But be that as it may, it is obvious that in the future all browsers will have to pull themselves up.

A working example can be seen at http://safron.pro/playground/html5uploader/ , below are only the most important pieces of code.

First, let's deal with the html-code. We will need the default input element, a container for dragging files and a list of ul, where we will place the thumbnails of the images:
<div> <input type="file" name="file" id="file-field" multiple="true" /> </div> <div id="img-container"> <ul id="img-list"></ul> </div> 


Nothing special, except that attribute multiple="true" is specified for the input element. This is necessary so that in the standard file selection dialog you can select several of them at once. By the way, starting with Firefox 4, browser developers promise that the standard file selection fields that are hated by many layout makers can be hidden and the dialog shown by triggering a click event for a hidden item.

Now let's move on to JavaScript (note that I used jQuery to simplify DOM manipulations. Anyone who, for whatever reason, wants to abandon jQuery, can easily redo scripts in such a way as to do without it). At first we will save in variables references to html-elements, starring in the main roles. Next, we define event handlers for the standard file selection field and for the area where files can be dragged.
  //  input   var fileInput = $('#file-field'); // ul-,     var imgList = $('ul#img-list'); // ,      drag and drop var dropBox = $('#img-container'); //        fileInput.bind({ change: function() { displayFiles(this.files); } }); //   drag and drop      dropBox dropBox.bind({ dragenter: function() { $(this).addClass('highlighted'); return false; }, dragover: function() { return false; }, dragleave: function() { $(this).removeClass('highlighted'); return false; }, drop: function(e) { var dt = e.originalEvent.dataTransfer; displayFiles(dt.files); return false; } }); 


In both cases, in the handler, we get access to the FileList object, which is essentially an array of File objects. This array is passed to the displayFiles () function, the text of which is shown below.
  function displayFiles(files) { $.each(files, function(i, file) { if (!file.type.match(/image.*/)) { //    return true; } //   li     ,   progress bar, //      file,    File (  ) var li = $('<li/>').appendTo(imgList); $('<div/>').text(file.name).appendTo(li); var img = $('<img/>').appendTo(li); $('<div/>').addClass('progress').text('0%').appendTo(li); li.get(0).file = file; //   FileReader     ,     //     var reader = new FileReader(); reader.onload = (function(aImg) { return function(e) { aImg.attr('src', e.target.result); aImg.attr('width', 150); /* ...      ... */ }; })(img); reader.readAsDataURL(file); }); } 


The File object contains metadata about the file, such as its name, size, and type (in MIME format, for example, image / gif), respectively, in the name, size, and type properties. To access the contents of the file, there is a special FileReader object.

Inside the displayFiles () function, we go through the transmitted array of files and first we filter out those that are not images. Then, for each image, a li list item is created, where an empty img element is placed (note that in each li element, a file property is also created that contains the corresponding object). Then an instance of FileReader is created and an onload handler is defined for it, in which data is passed directly to the src attribute of the img element created earlier. The readAsDataURL () method of the FileReader object takes a File object as a parameter and starts reading data from it. As a result, for all images selected via a standard field or dragged directly into the browser, we see their thumbnails (artificially reduced to 150 pixels).

What is left to do? It remains only to realize the very loading of all selected files on the server. To do this, create a button or link, when clicked, you can only run through all the created li elements, read their file property and pass it to the uploadFile () function, the text of which is shown below. I note that here, to simplify, I implemented loading via a function, and in a real example located at http://safron.pro/playground/html5uploader/ , I collected all the loading actions into the uploaderObject object, during the creation of which you can pass additional parameters , such as callback functions for information about the boot process.
 function uploadFile(file, url) { var reader = new FileReader(); reader.onload = function() { var xhr = new XMLHttpRequest(); xhr.upload.addEventListener("progress", function(e) { if (e.lengthComputable) { var progress = (e.loaded * 100) / e.total; /* ...      ... */ } }, false); /* ...     load  error  xhr.upload ... */ xhr.onreadystatechange = function () { if (this.readyState == 4) { if(this.status == 200) { /* ...  !   this.responseText ... */ } else { /* ... ! ... */ } } }; xhr.open("POST", url); var boundary = "xxxxxxxxx"; //   xhr.setRequestHeader("Content-Type", "multipart/form-data, boundary="+boundary); xhr.setRequestHeader("Cache-Control", "no-cache"); //    var body = "--" + boundary + "\r\n"; body += "Content-Disposition: form-data; name='myFile'; filename='" + file.name + "'\r\n"; body += "Content-Type: application/octet-stream\r\n\r\n"; body += reader.result + "\r\n"; body += "--" + boundary + "--"; if(xhr.sendAsBinary) { //   firefox xhr.sendAsBinary(body); } else { // chrome (   W3C) xhr.send(body); } }; //   reader.readAsBinaryString(file); } 


Here we create an instance of the FileReader object already familiar to us, just as above; an onload event handler is assigned to it, in which an XMLHttpRequest is created (unfortunately, you cannot use the jQuery ajax interface yet, since it does not yet provide for downloading files). In the second version of XMLHttpRequest, an upload property appeared that contains a loader object that can handle the progress, load and error events (see http://www.w3.org/TR/XMLHttpRequest2/#xmlhttprequesteventtarget for details). In the example above, only the handling of the progress event is shown. Next, we assign the request completion handler to the request itself (unlike the loader object events, it is called when all data is loaded and the response from the server is received), add two additional headers and generate the request body by reading the data from the result property of the FileReader object. After that, the download starts. I will only note that, according to the current W3C specification, the send () method of the XMLHttpRequest object can accept binary data as a parameter, which was successfully implemented in Google Chrome, however, Firefox did it in its own way, using the special sendAsBinary () method. Therefore, before sending, we check whether the sendAsBinary () method is defined in the request object, and, if so, use it.

That's all. We look forward to the approval and distribution of html 5!

Some links

  1. http://safron.pro/playground/html5uploader/ is a working example of what was described above (plus one more thing)
  2. http://safron.pro/playground/html5uploader/full.zip - the entire code is in the archive
  3. http://html5test.com - checking browsers for compliance with html 5 (very clearly)
  4. http://playground.html5rocks.com - platform for experiments with code from Google (its interface will be familiar to those who used numerous Google APIs)


UPD
To simplify the use of all of the above, a jQuery plugin was created. With it, you can upload files via the File API, where possible, and implement a replacement (for example, a regular form submission) where there is none. At the request of workers and correlated with the comments of commentators , loading was added through the FormData object in browsers that support it (Chrome, Safari 5+, FF 4+). At the very top of the file with the plugin there is a description of the parameters, methods, as well as brief examples of use. A more complete example of use can be seen here (this is the initial example from this article, only converted to use a plugin, its full code, including the server part, can be downloaded here [see UPD2]).

Used sources

  1. https://developer.mozilla.org/en/using_files_from_web_applications - an article about the file interface on the Mozilla developers website
  2. https://developer.mozilla.org/En/XMLHttpRequest/Using_XMLHttpRequest - article about using XMLHttpRequest ibid
  3. http://www.w3.org/TR/FileAPI/ - the current File API specification on the W3C website
  4. http://www.w3.org/TR/XMLHttpRequest2/ - current XMLHttpRequest specification in the same place



UPD2
At the request of the user glebovgin, the plugin has been finalized so that you can send not only the File object itself, but also Blob data (Blob object). This can be useful if there is a need to send to the server, for example, the contents of the canvas, well, or just manually generated data.

In the demo (which moved to a slightly different address ) an example of sending a picture from canvas was added. At the moment, this feature works in FF, Chrome, IE10.

The source code is now available on GitHub . Comments, suggestions, improvements are welcome!

Source: https://habr.com/ru/post/109079/


All Articles