Hello! Today we have the following task: it is necessary to create an interface for uploading pictures, which would be generated before downloading a thumbnail of a small format. At the moment, HTML5 is sweeping across the planet, and it would seem that how to implement this should be very clear. There are several Russian-language articles on this topic (for example, for example). But there is one thing. In the approach considered there, no attention is paid to the memory consumption of the browser. And the expense can reach the gigantic sizes. Of course, if you download at the same time no more than 5-10 pictures of a small format, then everything remains within the normal range; but our interface should allow loading at once many images of a format not less than that of modern cameras-soap cases. And then the free memory begins to melt before our eyes.
To begin with, in order to assess the scale of the problem, we will
implement the approach described with virtually no changes in all articles on this topic, and try to follow the memory usage. I tried to make the code of examples as simple as possible to demonstrate the creation of a preview. How to implement Drag & Drop and download can be viewed at least even in my previous
articleCodevar listen = function(element, event, fn) { return element.addEventListener(event, fn, false); }; listen(document, 'DOMContentLoaded', function() { var fileInput = document.querySelector('#file-input'); var listView = document.querySelector('#list-view'); listen(fileInput, 'change', function(event) { var files = fileInput.files; if (files.lenght == 0) { return; } for(var i = 0; i < files.length; i++) { generatePreview(files[i]); } fileInput.value = ""; }); var generatePreview = function(file) { var reader = new FileReader(); reader.onload = function(e) { var dataUrl = e.target.result; var li = document.createElement('LI'); var image = new Image(); image.width = 100; image.onload = function() {
For tests, I used a folder of unremarkable photos of 3648x2736 pixels and an average size of 4 megabytes. As well as a set of current version of browsers: Chrome (31.0), Yandex (13.12), Firefox (26.0), and IE (11.0.1). Well, the usual Task Manager (Win 8.1).
')
So, we select 20 photos in the field. We look:
Browser | Memory consumption, MB |
---|
Chrome | 994 |
Yandex | 1045 |
Firefox | 1388 |
IE | 1080 |
Two points are worth noting here: 1) Yandex and Chrome keep a separate process for each tab, but Firefox and IE do not, therefore for the last two some overhead costs not directly related to our testing also fall into the measurements; 2) I took measurements (hereafter) approximately 20 seconds after loading all the images to allow browsers to free up memory in hot pursuit, which they do, albeit very slightly, within 50MB, i.e. continue to hold still too large volumes. After updating / closing the page, all browsers slowly free up memory to normal volumes.
So, it is clear that this situation does not suit us in a decisive way. We think that we can take ...
The first approach to the projectile
My first thought was: “what if such an overrun comes from trying to load all the pictures in parallel? Maybe try to do it consistently, thereby giving browsers a chance to catch a little breath? ”. Well, we try to
implement the simplest queue.
Results (on the same 20 photos):Browser | Memory consumption, MB |
---|
Chrome | 979 |
Yandex | 1119 |
Firefox | 1360 |
IE | 399 |
We see that it helped only in the case of IE. Well, what to do - we recommend that all users abandon the use of any browsers other than IE. Joke. We think further ...
Second approach
After a session of some shamanism, the thought comes to mind: “maybe the problem is that browsers have to keep huge images in memory, although in fact we only need to shrink the image to the size of the preview? What if instead of the usual img we use canvas, where should I put the already compressed image? ”.
So do .
Code var queue = []; var isProcessing = false; listen(fileInput, 'change', function(event) {
Results (all the same 20 pictures):Browser | Memory consumption, MB |
---|
Chrome | 188 (at peak times reached ~ 800MB, but quickly threw off) |
Yandex | 201 (at peak times reached ~ 1GB, but immediately threw off, like Chrome) |
Firefox | 661 (peak ~ 900. It should be noted that after waiting a minute more, threw up to 300) |
IE | 103 (peak ~ 260) |
Despite the high consumption in the process (for everyone except IE), browsers at least started immediately freeing memory. It can not but rejoice. But to celebrate the final victory is still too early. We think that you can still take ...
Third approach
In the process of further throwing and not too successful experiments, we recall that once came across an API such as ObjectURL (
creation and
recycling ), which allows you to create local links to any binary data stored in the browser's cache, as well as to recycle them. In theory, this can help us avoid processing giant DataURLs.
Rather, we tryResults:Browser | Memory consumption, MB |
---|
Chrome | 881 |
Yandex | 927 |
Firefox | 140 (peak ~ 860) |
IE | 36 (peak ~ 70) |
What did we get? Well, firstly, excellent results in IE. More or less acceptable in FF. But with WebKit browsers, they seemed to be bounced back. For the sake of fairness, it should be noted that in all browsers, images began to be processed faster purely on sensations, but at the same time, short-term friezes appeared in IE. It is also possible that, frankly, FF and IE immediately release resources after calling URL.revokeObjectURL (), and webkit browsers need some time to do this (it is even possible that they will be faster to do this in conditions of low memory). Then you can go in two ways: 1) divide the approaches - in the webkite browsers return to the second approach (with this, everything is clear - a matter of technology); and 2) try to bring the third approach everywhere. Let's try the last option ...
Approach the fourth (and last): what else is it to optimize?
A little pushing, squeeze out a couple of improvements. First, we remove the creation of the img element from the queue handler: now we will reuse the same pre-created object. Looking ahead, I’ll say that it helped to significantly improve the memory situation in webkits of
online browsers - as required. And second, this is a well-known trick - we postpone every next processing with setTimeout (), this helped to improve the situation with short-term friezes. So, the
result :
Testing:Browser | Memory consumption, MB |
---|
Chrome | 103 (peak ~ 150) |
Yandex | 113 (peak ~ 150, as in Chrome) |
Firefox | 107 (peak ~ 510) |
IE | 40 (and it’s not raised above. As if there was no work at all) |
At the same time we will also test on 100 pictures of the same size:Browser | Memory consumption, MB |
---|
Chrome | 98 (peak ~ 150) |
Yandex | 150 (peak ~ 180) |
Firefox | 104 (peak ~ 520) |
IE | 40 (all the same 40MB!) |
We see that the increase in the number of processed images practically does not increase consumption. On this, perhaps, dwell.
Conclusion
To admit, after the very first surveys, at some point I thought that in the present state of affairs it would not be possible to realize this possibility without excessive memory overruns. Still, it is ugly to suspend the user [who wished to upload 100 pictures at once] of his hypothetical netbook. But it is nice that we managed to overcome these doubts :)
So, we managed to figure out a few points. Number one: using DataURL is only suitable for working with very small format images (for larger ones, it is preferable to use the objectURL API, which consists of only two methods). Number two: be careful with creating a large number of Image objects. Number three: do not do all processing at the same time.
Well, since this small study inexplicably led to a comparison of browsers, let's go over each one individually.
Firefox lost?
Despite the peak consumption in comparison with the others, I think that the situation is quite acceptable. Firstly (I did not mention it above), even 30 seconds after the measurements, the memory dropped to 60MB, which is even lower compared to webkit ones; and secondly, it is likely that, in the conditions of a severe shortage, Firefox would periodically clean up the memory during processing and, in the end, even at the peak would not eat off so much. In general, we set off.
Equalize with IE!
This is a joke again :) But if we speak objectively, then we must admit that IE now is not just a tool for downloading a normal browser, but at least one more good browser.
Is Yandex Browser a bit behind the older brothers?
I do not think. And here's why: the fact is that it is now used as the main one for me, and therefore the experiment cannot be called crystal clear. A couple of plugins, history, synchronization - all this could well cause this slight lag.
And where is the Opera?
I really did not want to put the 12th version separately for this experiment. Despite the fact that it is still being used by some people, soon this number of loyal fans will either have to be updated or migrated to another browser. And about the new, webkitova - there is every reason to believe that the situation is similar to Yandex and Chrome.
UPD: I checked all the same in the 12th Opera. The results are as follows: the 3rd (and, respectively, the 4th) approaches do not work. The second approach eats away 500MB at the peak and 300MB after the end of processing.
But what about Safari?
On win, this is a rare beast, but on a poppy it tested the final version (all in the same 20 photos). During processing, the memory consumption did not increase at all.
What with mobile browsers?
Checked also in Safari on iPhone 5S. There is a short-term frieze, but at the same time, the amount of free memory has practically not decreased. I did not find out right away whether it was possible to somehow see how much each process specifically reserves, I would be grateful if someone tells you in the kamentah. Devices on Android, unfortunately, was not at hand. Perhaps someone is not too lazy to check on their own and share the results.
Thanks for attention. I hope someone will help the article not to waste time on similar research. And with the past holidays you,% username%!