如何上传/发布多个画布元素

8
我需要为未来的项目创建一个图片上传工具(不使用flash,IE10+,FF7+等),客户端可以进行图片调整、转换和裁剪,而无需在服务器上进行。
因此,我创建了一个JavaScript界面,用户可以在浏览器中“上传”文件并直接缩放或裁剪,而不必与服务器联系。性能还可以,虽然不是很好,但能正常工作。
最终结果是一组canvas元素。用户可以在调整大小后编辑/裁剪图像,因此我将它们保留为canvas而不是转换为JPEG(这会降低初始性能)。
现在这个工具运行良好,但我不知道如何将已完成的canvas元素上传到服务器。 (使用服务器上的asp.net 4通用处理程序)
我已经尝试从包含每个画布数据URL的所有元素创建JSON对象。
问题是,当我拥有10-40张照片时,浏览器在创建数据URL时开始冻结,特别是对于大于2兆字节的图像。
            //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);
            }

同时将它们转换为json对象 (我使用json2.js),通常会导致我的浏览器崩溃。(FF7)

我的对象

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

上传程序
            //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
            });

如何将画布元素发送到服务器是最佳方式?

我应该一次性全部发送还是逐个发送?


2
这是一个非常有趣的问题。不确定为什么会有两个踩但没有任何评论,+1。 - David Houde
1
这个问题为什么会被投反对票呢? - Pointy
生成数据URL并逐个发送。这将减少内存使用量。它还可以让您显示进度(如果不支持xhr2)。另一个好处是,服务器逻辑会稍微简单一些,并且服务器上的内存使用量会更少。 - Gerben
1个回答

5

逐个上传文件更好。这样需要的内存更少,一旦一个文件准备好上传,就可以开始上传,而不必等待所有文件都准备好。

使用FormData发送文件。它允许以二进制格式上传文件,而不是以base64编码的格式。

var formData = new FormData;

如果使用Firefox,建议使用canvas.mozGetAsFile('image.jpg')而不是canvas.toDataUrl()。这样可以避免将base64转换为二进制码的不必要操作。

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

在Chrome中,可以使用BlobBuilder将base64转换为blob(请参见dataURItoBlob函数)。
经过一番尝试,我自己成功地解决了这个问题。
首先,以下代码可以将dataURI转换为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});
}

来自这个问题

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

然后发送formData对象。我不确定如何在jQuery中实现,但使用普通的xhr对象可以这样做:

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

在服务器上,您可以从文件集合中获取文件:

context.Request.Files[0].SaveAs(...);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接