6

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.

  1. User uploads file using the standard file input HTML element:

    <input class="cancel" type="file" name="userFile" id="userFile" accept="image/*"/>

  2. 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.

  3. 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

  1. 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

  2. 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

  1. 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.

  2. 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.

  3. Resizing the img directly, but this does not change the underlying data size that needs to be uploaded.

  4. 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.

Community
  • 1
  • 1
acarlon
  • 16,764
  • 7
  • 75
  • 94

1 Answers1

0

What I assume you are trying to do is the following

  1. User uploads image
  2. The client displays a reduced quality image to the user.
  3. The client then uploads that reduced quality image to your server for later use.

The problem you are running into is you are forcing javascript (the client) to do things that the would most likely be best done on the server side. As for displaying the image at a lower resolution that can be easily attained by using drawImage. There is no reason to display to the user the actual reduced file immediately on the client side. Here is my version of your use flow.

  1. User uploads image.
  2. Client uploads it to a server while displaying a reduced resolution version using drawImage.
  3. The server runs a script much like this one resizing it to the requested size.
  4. Then when the image needs to be used in the future the server coughs up the resized version.

I hope this helps!!!

hyphnKnight
  • 303
  • 2
  • 6
  • Thanks, but drawImage is the cause of the blocking ('see cause of blocking' point 2). This isn't only for resizing, but also for creating composite images. The benefit of doing this client side is reduced load on the server, some of the images might not even be uploaded to server. The problem is that browsers seem to do operations on the UI thread that would be best done on a worker thread. – acarlon May 29 '14 at 09:06
  • If the files are so large that they are slowing down the main thread then those are way to large as I have never ran into this issue, when you say blocking I assume you mean its slowing down. You won't be able to run this through a webworker because doing so would require you to use getDATAURL which will be blocked by the browsers security software. Could you update your question with the approximate use of this code and the size of the files you are dealing with? – hyphnKnight May 29 '14 at 15:55
  • The first sentence of my questions already has the size of the code (2MB+). getDataUrl is fine on the main thread, rather it is the resizing and assignment of src that should be done on a separate worker (though not currently possible) and ideally done by the browser. Looking at the firefox source code, this just seems to be the way that things work and without a good javascript image compression library, I will just have to accept it for the moment. – acarlon May 30 '14 at 09:16