Problem
I can successfully achieve the following steps, but the UI can become unresponsive for files of 2 MB or more. These files are in the size range of what a user might upload from their camera or desktop, so I will need to handle them.
User uploads file using the standard file input HTML element:
<input class="cancel" type="file" name="userFile" id="userFile" accept="image/*"/>
On the client side I resize the file so that the width is 500px or less (specifically to reduce the image size for large files) and show the image.
On the client side I upload the resized smaller file.
Demo
See this fiddle.
There you can see that the -
stops moving when uploading larger files. The delay seems more significant in Chrome than Firefox, but it still noticeable on Firefox.
Cause of blocking
When the file data URL is set on the img src. This is needed so that the image can be resized in the canvas.
img.src = e.target.result;//blocking here
When the img is written to the canvas. This is needed to resize the image.
ctx.drawImage( img, 0, 0, width, height );//blocking here
Options I have considered
Doing the blocking actions in a Web Worker. This is not possible because Web Workers cannot manipulate DOM elements and the blocking calls both occur when manipulating the DOM.
Resizing the raw image data in a Web Worker. Unfortunately, I don't know what the format of the img will be so resizing will need to handle all image types. I am not aware of any JavaScript library that will do this and I don't particularly want to write my own cross-format image compression library.
Resizing the img directly, but this does not change the underlying data size that needs to be uploaded.
Doing the resizing on the server is not an option because I want to reduce the upload size. Resizing on the client is also better since the memory for the large image is released early and the resized image is displayed immediately.
Other thoughts
I know that setting the img src is async, so I guess that it is the copying of the data that is causing the blocking. I wonder if there is a way to share the same data rather than doing a copy?
I am using what I believe to be a standard approach of using FileReader
to get a data URL, then set the data URL to an img.src
and canvas.drawImage
to resize (there are many examples of this, one being here). Any assistance in improving the responsiveness of this approach or another approach would be appreciated.