user2501507
user2501507

Reputation:

Slow HTML5 File Upload?

I compared the VERSION1: standard html file upload with VERSION2: a html5 fileApi and ajax upload. It turns out that the html5 fileApi and ajax upload is much much slower than the old html file upload.

  1. Why is the upload in version 2 so much slower than in version1?
  2. How can I accelerate the upload in version2?

VERSION1:

HTML

<g:form method="post" accept-charset="utf-8" enctype="multipart/form-data"  
     name="imageUploaderForm" id="imageUploaderForm" url="someurl">

    <input type="file" accept="image/jpeg, image/gif, image/png" 
            name="image" id="image" />

</form>

JS (I use JQueryForm)

$("#image").change(function() {

  $("#imageUploaderForm").ajaxForm({
    complete: function(response){
        console.log("upload complete");
    }
  });
  $("#imageUploaderForm").submit();

});

Server code Grails 2.2.4:

CommonsMultipartFile file = (CommonsMultipartFile) request.getFile('image')
byte [] imageBytes = file.getBytes()

VERSION2:

HTML

<g:form method="post" accept-charset="utf-8" enctype="multipart/form-data"  
         name="imageUploaderForm" id="imageUploaderForm" url="someurl"></form>

<input id="UploadFileInput" class="UploadFileInput" type="file" name="image" accept="image/jpeg, image/gif, image/png" />

JS (I use filereader.js which just wraps the filereader api for jquery) I loaded the uploaded image into a html5 canvas because I need to manipulate the image before uploading.

var fileReaderOpts = {
        readAsDefault: 'BinaryString',
        on: {
            load: function(event, file) {

                var $img = $('<img>'), 
                    imgWidth, imgHeight;

                $img.load(function() {

                    // Create the canvas.
                    $originalCanvas = $('<canvas data-caman-hidpi-disabled>');
                    var originalContext = $originalCanvas[0].getContext('2d');  

                    // Save image to canvas
                    $originalCanvas[0].width = this.width;
                    $originalCanvas[0].height = this.height;
                    originalContext.drawImage(this, 0, 0);

                    // some image modification on the canvas

                    // send image to server

                    var imageUrl = $originalCanvas[0].toDataURL();

                    $("#imageUploaderForm").ajaxForm({
                        data: { 
                            img : imageUrl,
                        },
                        complete: function(response){
                                console.log("upload complete");
                        } 
                    }); 

                    $("#imageUploaderForm").submit();



                }); // end $img.load

                // Set the src of the img, which will trigger the load event when done
                $img.attr('src', event.target.result);


            },// end load           
            beforestart: function(file) {
                // Accept only images. Returning false will reject the file.
                return /^image/.test(file.type);
            }
        }
};


// Bind the fileReader plugin the upload input and the drop area.
$("UploadFileInput").fileReaderJS(fileReaderOpts);

Server code Grails 2.2.4:

String imgBase64 = params.image
imgBase64 = imgBase64.trim().replaceFirst("data:image/png;base64,", "")
byte[] imageBytes = Base64.decode(imgBase64.getBytes())

Here is what I have measured:

I have uploaded a jpg image with 7.5MB size with both version1 and version2, and also with pinterest and flickr. I started the timer for version1 and version2 after the image has been processed on the client side at the moment where the form is submitted.

Note: The canvas-related code is not included in the time. I started after this with the measuring.

The result:

Upvotes: 3

Views: 4722

Answers (1)

Daniel
Daniel

Reputation: 3131

Version 2 Base64 encodes the data in toDataUrl(). This is going to be a larger amount of data sent over the wire than Version 1, which is just sending raw binary. To see this, use Fiddler to watch the HTTP traffic and compare the two. The Statistics tab will show you 'Bytes Sent', which I think will be more with your version 2 approach. When measuring, be careful where you start and stop your timer. I know you say in comments that you exclude the canvas work, but unless you are measuring from after toDataUrl() up until complete fires, it's not a "fair" comparison of network time. In short, it's slower because a) you're sending more data and b) toDataUrl has to copy the image from canvas to the form.

As far as how to make it faster, that's a little more tricky to do and keep your features. You are doing more work on the client, so it's necessarily going to be slower. AFAIK, canvas doesn't have a way to stream the data as raw bytes (not base64) to the server, which is what you'd want. jQuery File Upload may help at least make it more user-friendly, or may have some magic I'm not clear on to send the data some way besides base64: https://github.com/blueimp/jQuery-File-Upload/wiki/Client-side-Image-Resizing.

Depending on what you're doing in canvas and your server resources, it may make more sense to do the image processing on the server. That would certainly make the upload faster, at the expense of server CPU and memory.

Upvotes: 1

Related Questions