📜 ⬆️ ⬇️

File upload via FileReader

Why did I make another bike?


Working on one of the current projects again faced the need to implement convenient and fast file downloads. With a familiar gesture, he uncovered the plupload , but then he thought.

I heard about FileAPI a long time ago, at that time there was a problem with support in browsers, as a result of which, in any case, I would have to provide an alternative loader and the game as a whole was not worth the trouble. But since at the moment it was a question of a loader for the back-end of the site, it was possible to narrow the number of supported browsers and I decided to study the issue deeper.
')


On Habré there were two informative articles: Features of uploading files to HTML5 and Uploading files using the html5 File API, with preference and dancers . The second article is relatively old and the code given there, according to the author himself, is “tied to the project and its features”, and therefore requires a long doping. The first article, without any specific demo with ready-made code, but gave me a lot of food for thought and a rough direction in which to dig.

I have chosen the upload method via FileReader. At the moment, it is supported by all new versions of popular browsers ( more ). Including even Internet Explorer 10, as part of Windows 8, which, by the way, is just around the corner (October 26, the beginning of retail sales).

Where is the demo?


You can watch the demo here or download the archive .

What can a demo?


The demo can download up to 10 files simultaneously, no more than 5 MB each and no more than 50 MB in total. And this restriction is reset after downloading each batch of files. Shows a progress bar as it loads. Enables and disables buttons as long as there are or are no files in the list that can be accessed (load / delete).

The demo has a minimal “project-specific” code and it can be easily and quickly implemented in any project.

And how does it work?


The principle of operation, in principle, does not constitute Polichinel’s secret.

1) For drag-n-drop, which, by the way, works on all new versions of desktop browsers , we hang something like this on events:

$('.dropbox') .on('drop', function(event) { if (event.originalEvent.dataTransfer.files.length) { //        addFiles. event.preventDefault(); event.stopPropagation(); addFiles(event.originalEvent.dataTransfer.files); $(this).css('border-color', 'gray'); $(this).css('color', 'gray'); } }) .on('dragenter', function(event) { //     . $(this).css('border-color', 'green'); $(this).css('color', 'green'); }) .on('dragleave', function(event) { //  . $(this).css('border-color', 'gray'); $(this).css('color', 'gray'); }); 


2) Let us leave the possibility to add a file through standard input [type = file] by adding to it also the multiple attribute, which will allow us to select several files at once. One can not even think about its support by browsers (I remind you that this is a back-end, which is unlikely to be used in the old browser).

 <input type="file" name="file" size="1" multiple /> 


 $('input[type=file]').on('change', function(event) { addFiles(this.files); }); 


3) Actually the addFiles function itself, which was mentioned in the listings above. Here I will give it in part, omitting non-critical things, you can fully view it in the script from the archive (/js/FRUploader.js). This function is not the last resort, it only adds files to the list of selected ones, simultaneously checking the files for various restrictions (my demo provides for the configuration of the maximum file size, limits for the total files and their number):

 function addFiles(files) { $.each(files, function(i, v) { //     . if (v.size > maxfs) { maxfsFiles.push(v.name); } else if (flist.length >= maxfc) { $('div#maxfcerr').show('fast'); return false; } else if (maxts - curts < v.size) { $('div#maxtserr').show('fast'); return false; } else { //  . var fr = new FileReader(); fr.file = v; fr.readAsDataURL(v); //        . var row = document.createElement('tr'); /* *         ,       . */ $('table tbody').append(row); //     . flist.push({file: v, trnum: 'id' + index}); } }); } 


4) A function that directly uploads files to the server. Called by clicking on the Download button.

 function uploadFile(file, trnum) { if (file) { var xhr = new XMLHttpRequest(); upload = xhr.upload; //    progress,   "" -. upload.addEventListener('progress', function(event) { if (event.lengthComputable) { var pbar = $('tr.' + trnum + ' td.size div.pbar'); pbar.css('width', Math.round((event.loaded / event.total) * 100) + 'px'); } }, false); //    load,      - . upload.addEventListener('load', function(event) { var pbar = $('tr.' + trnum + ' td.size div.pbar'); pbar.css('width', '100px'); pbar.css('background', 'green'); }, false); //    error,     - . upload.addEventListener('error', function(event) { var pbar = $('tr.' + trnum + ' td.size div.pbar'); pbar.css('width', '100px'); pbar.css('background', 'red'); }, false); //  . xhr.open('POST', 'handler.php'); //  . xhr.setRequestHeader('Cache-Control', 'no-cache'); xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); xhr.setRequestHeader("X-File-Name", file.name); //  . xhr.send(file); } } 


5) And, of course, the handler on the server (handler.php). I hasten to remind someone who wants to take my code as a basis, which undoubtedly should not rely solely on checking files with JavaScript. On the server, you also need to check whether the file passes through all restrictions. In the demo example, this is omitted:

 if (!array_key_exists('HTTP_X_FILE_NAME', $_SERVER) || !array_key_exists('CONTENT_LENGTH', $_SERVER)) exit(); $fname = $_SERVER['HTTP_X_FILE_NAME']; $fsize = $_SERVER['CONTENT_LENGTH']; if (!$fsize) exit(); file_put_contents("upload/".$fname, file_get_contents("php://input")); 


As you can see, in the case of loading via FileReader, unlike FormData, we can read and write the file directly from stdin ( php: // input ).

I want the thumbnails!


Considering the various examples of the implementation of downloading files through FileAPI, I certainly undoubtedly met with building a preview before uploading an image. Technically, this is easy:

 if (file.type.search(/image\/.*/) != -1) { var thumb = new Image(); thumb.src = ev.target.result; thumb.addEventListener("load", function() { maxwidth = 120; maxheight = 90; if (thumb.width > thumb.height) { thumb.height = thumb.height / (thumb.width / maxwidth); thumb.width = maxwidth; } else { thumb.width = thumb.width / (thumb.height / maxheight); thumb.height = maxwidth; } }, false); thumb.load; td.appendChild(thumb); delete thumb; } 


But when I tried to download some very heavy photos (8 photos from 10 to 16 MB each), I had all the browsers dropped, except Opera 12.02, which after a very long deliberation did come to an end, but I reacted very slowly to any action.

This is due to the fact that the image preview is loaded via the data: / and base64 encoded content of the file. For the sake of the preview 120x90, the 16-megabyte image 5184x3456 is included on the page. For a very long time I tried to find at least some way to resize the resulting image on the fly and use it for thumbnails, but either JavaScript doesn’t know how or I don’t know how to search. If someone in the comments tells you how to solve the problem, I will be extremely grateful.

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


All Articles