我一直在尝试重新实现一个HTML5图像上传器,就像Mozilla Hacks网站上的那个,但它适用于WebKit浏览器。该任务的一部分是从canvas对象中提取图像文件,并将其附加到FormData对象中以便上传。

问题是,虽然canvas有toDataURL函数来返回图像文件的表示,但FormData对象只接受file API中的file或Blob对象。

Mozilla解决方案在画布上使用了以下仅限firefox的功能:

var file = canvas.mozGetAsFile("foo.png");

...这在WebKit浏览器上是不可用的。我能想到的最佳解决方案是找到某种方法将Data URI转换为File对象,我认为这可能是File API的一部分,但我无法找到这样做的方法。

这可能吗?如果没有,有什么替代方案吗?


当前回答

我和拉文德·帕亚尔遇到了同样的问题,而我找到了答案。试试这个:

var dataURL = canvas.toDataURL("image/jpeg");

var name = "image.jpg";
var parseFile = new Parse.File(name, {base64: dataURL.substring(23)});

其他回答

toDataURL给你一个字符串,你可以把这个字符串放到一个隐藏的输入中。

Stoive的原始答案很容易通过修改最后一行来适应Blob:

function dataURItoBlob (dataURI) {
    // convert base64 to raw binary data held in a string
    // doesn't handle URLEncoded DataURIs
    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 an ArrayBuffer
    var ab = new ArrayBuffer(byteString.length);
    var ia = new Uint8Array(ab);
    for (var i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
    }

    // write the ArrayBuffer to a blob, and you're done
    return new Blob([ab],{type: mimeString});
}

在摆弄了一些东西之后,我自己设法弄明白了这一点。

首先,这将把一个dataURI转换为一个Blob:

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 dataURL = canvas.toDataURL('image/jpeg', 0.5);
var blob = dataURItoBlob(dataURL);
var fd = new FormData(document.forms[0]);
fd.append("canvasImage", blob);

Firefox有canvas.toBlob()和canvas.mozGetAsFile()方法。

但是其他浏览器没有。

我们可以从canvas中获取dataurl,然后将dataurl转换为blob对象。

这是我的dataURLtoBlob()函数。它很短。

function dataURLtoBlob(dataurl) {
    var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
    while(n--){
        u8arr[n] = bstr.charCodeAt(n);
    }
    return new Blob([u8arr], {type:mime});
}

使用此函数与FormData一起处理画布或dataurl。

例如:

var dataurl = canvas.toDataURL('image/jpeg',0.8);
var blob = dataURLtoBlob(dataurl);
var fd = new FormData();
fd.append("myFile", blob, "thumb.jpg");

同样,你也可以为非gecko引擎浏览器创建HTMLCanvasElement.prototype.toBlob方法。

if(!HTMLCanvasElement.prototype.toBlob){
    HTMLCanvasElement.prototype.toBlob = function(callback, type, encoderOptions){
        var dataurl = this.toDataURL(type, encoderOptions);
        var bstr = atob(dataurl.split(',')[1]), n = bstr.length, u8arr = new Uint8Array(n);
        while(n--){
            u8arr[n] = bstr.charCodeAt(n);
        }
        var blob = new Blob([u8arr], {type: type});
        callback.call(this, blob);
    };
}

现在canvas.toBlob()不仅适用于Firefox,还适用于所有现代浏览器。 例如:

canvas.toBlob(
    function(blob){
        var fd = new FormData();
        fd.append("myFile", blob, "thumb.jpg");
        //continue do something...
    },
    'image/jpeg',
    0.8
);

不断发展的标准看起来是canvas.toBlob(),而不是Mozilla冒险猜测的canvas.getAsFile()。

我还没有看到任何浏览器支持它:(

谢谢这个很棒的帖子!

此外,任何人尝试接受的答案应该小心BlobBuilder,因为我发现支持是有限的(和命名空间):

    var bb;
    try {
        bb = new BlobBuilder();
    } catch(e) {
        try {
            bb = new WebKitBlobBuilder();
        } catch(e) {
            bb = new MozBlobBuilder();
        }
    }

你在BlobBuilder中使用了其他库的polyfill吗?