Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to upload/POST multiple canvas elements

I have to create an image uploader for a future project (No flash, IE10+, FF7+ etc.) that does image resizing/converting/cropping on the clientside and not on the server.

So I made a javascript interface where the user can 'upload' their files and get resized/cropped in the browser directly, without ever contacting the server. The performance is OK, not that good, but it works.

The endresult is an array of canvas elements. The user can edit/crop the images after they got resized, so I keep them as canvas instead of converting them to jpeg. (Which would worsen the initial performance)

Now this works fine, but I don't know what's the best way to actually upload the finished canvas elements to the server now. (Using a asp.net 4 generic handler on the server)

I have tried creating a json object from all elements containing the dataurl of each canvas.

The problem is, when I got 10-40 pictures, the browser starts freezing when creating the dataurls, especially for images that are larger than 2 megabyte.

            //images = array of UploadImage
            for (var i = 0; i < images.length; i++) {
                var data = document.getElementById('cv_' + i).toDataURL('image/jpg');
                images[i].data = data.substr(data.indexOf('base64') + 7);
            }

Also converting them to a json object (I am using json2.js) usually crashes my browser. (FF7)

My object

    var UploadImage = function (pFileName, pName, pDescription) {
        this.FileName = pFileName;
        this.Name = pName;
        this.Description = pDescription;
        this.data = null;
    }

The upload routine

            //images = array of UploadImage
            for (var i = 0; i < images.length; i++) {
                var data = document.getElementById('cv_' + i).toDataURL('image/jpg');
                images[i].data = data.substr(data.indexOf('base64') + 7);
            }

            var xhr, provider;
            xhr = jQuery.ajaxSettings.xhr();
            if (xhr.upload) {
                xhr.upload.addEventListener('progress', function (e) {
                    console.log(Math.round((e.loaded * 100) / e.total) + '% done');
                }, false);
            }
            provider = function () {
                return xhr;
            };
            var ddd = JSON.stringify(images); //usually crash here
            $.ajax({
                type: 'POST',
                url: 'upload.ashx',
                xhr: provider,
                dataType: 'json',
                success: function (data) {
                    alert('ajax success: data = ' + data);
                },
                error: function () {
                    alert('ajax error');
                },
                data: ddd
            });

What would be the best way to send the canvas elements to the server?

Should I send them all at once or one by one?

like image 650
elch Avatar asked Oct 10 '11 12:10

elch


1 Answers

Uploading files one by one is better. Requires less memory and as soon as one file ready to upload, the upload can be started instead of waiting while all files will be prepared.

Use FormData to send files. Allows to upload files in binary format instead of base64 encoded.

var formData = new FormData;

If Firefox use canvas.mozGetAsFile('image.jpg') instead of canvas.toDataUrl(). Allow to avoid unnecessary conversion from base64 to binary.

var file = canvas.mozGetAsFile('image.jpg');
formData.append(file);

In Chrome use BlobBuilder to convert base64 into blob (see dataURItoBlob function

accepted After playing around with a few things, I managed to figure this out myself.

First of all, this will convert a dataURI to a Blob:

//added for quick reference
function dataURItoBlob(dataURI) {
    // convert base64/URLEncoded data component to raw binary data held in a string
    var byteString;
    if (dataURI.split(',')[0].indexOf('base64') >= 0)
        byteString = atob(dataURI.split(',')[1]);
    else
        byteString = unescape(dataURI.split(',')[1]);

    // separate out the mime component
    var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];

    // write the bytes of the string to a typed array
    var ia = new Uint8Array(byteString.length);
    for (var i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
    }

    return new Blob([ia], {type:mimeString});
}

From this question):

var blob = dataURItoBlob(canvas.toDataURL('image/jpg'));
formData.append(blob);

And then send the formData object. I'm not sure how to do it in jQuery, but with plain xhr object it like so:

var xhr = new XMLHttpRequest;
xhr.open('POST', 'upload.ashx', false);
xhr.send(formData);

On server you can get files from Files collection:

context.Request.Files[0].SaveAs(...);
like image 195
Andrey M. Avatar answered Sep 28 '22 18:09

Andrey M.