将数据 URI 转换为文件,然后附加到 FormData

IT技术 javascript html webkit
2021-01-10 01:39:54

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

问题是虽然canvas具有toDataURL返回图像文件表示函数,但 FormData 对象仅接受来自File API 的File 或 Blob 对象

Mozilla 解决方案在 上使用了以下仅限 Firefox 的功能canvas

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

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

是否可以?如果没有,有什么替代方案吗?

谢谢。

6个回答

在玩了几件事之后,我设法自己解决了这个问题。

首先,这会将 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);
@stoive 在那种情况下,为什么它不是 bb.append(ia)?
2021-03-12 01:39:54
为什么总是发生这种情况......您试图通过到处搜索来解决问题几个小时。然后你发布一个问题。在一小时内,您会从另一个问题中得到答案。不是我在抱怨... stackoverflow.com/questions/9388412/ ...
2021-03-15 01:39:54
@stoive我能够去构造斑点,但可以请你解释一下你如何构建POSTPUT到S3?
2021-03-28 01:39:54
谢谢!这通过一个小的更正解决了我的问题var file = new File( [blob], 'canvasImage.jpg', { type: 'image/jpeg' } ); fd.append("canvasImage", file);
2021-04-06 01:39:54
@mimo - 它指向底层ArrayBuffer,然后将其写入BlobBuilder实例。
2021-04-09 01:39:54

BlobBuilder 和 ArrayBuffer 现在已弃用,以下是使用 Blob 构造函数更新的顶部注释代码:

function dataURItoBlob(dataURI) {
    var binary = atob(dataURI.split(',')[1]);
    var array = [];
    for(var i = 0; i < binary.length; i++) {
        array.push(binary.charCodeAt(i));
    }
    return new Blob([new Uint8Array(array)], {type: 'image/jpeg'});
}
“二进制”这个名字有点误导,因为它不是位数组,而是字节数组。
2021-03-16 01:39:54
首先创建 Uint8Array 比创建一个 Array 然后转换为 Uint8Array 更好。
2021-03-20 01:39:54
在 Safari 上对我来说也失败了。@威廉T。不过,'s answer 适用于 Firefox/Safari/Chrome。
2021-03-25 01:39:54
只是一个想法:array=[]; array.length=binary.length;......array[i]=bina等所以数组是预先分配的。它节省了每次迭代都必须扩展数组的 push(),并且我们在这里处理可能数百万个项目(=字节),所以这很重要。
2021-04-03 01:39:54
"type: 'image/jpeg'" - 如果它是 png 图像或者如果您事先不知道图像扩展名怎么办?
2021-04-04 01:39:54

这个适用于 iOS 和 Safari。

您需要使用 Stoive 的 ArrayBuffer 解决方案,但不能使用 BlobBuilder,如 vava720 所示,所以这里是两者的混搭。

function dataURItoBlob(dataURI) {
    var byteString = atob(dataURI.split(',')[1]);
    var ab = new ArrayBuffer(byteString.length);
    var ia = new Uint8Array(ab);
    for (var i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
    }
    return new Blob([ab], { type: 'image/jpeg' });
}
带有 webkit 前缀的 iOS6 回退是什么?你怎么处理这个?
2021-03-20 01:39:54
伟大的!但是你仍然可以保持 mime 字符串动态,就像在 Stoive 的解决方案中一样,我想?// 分离出 mime 组件 var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]
2021-04-04 01:39:54

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

但其他浏览器没有。

我们可以从画布中获取 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");

此外,您可以HTMLCanvasElement.prototype.toBlob为非 Gecko 引擎浏览器创建一个方法。

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() 适用于所有现代浏览器。”
2021-03-21 01:39:54
这里提到的 canvas.toBlob 的 polyfill 是处理这个问题的正确方法恕我直言。
2021-03-29 01:39:54

我的首选方式是canvas.toBlob()

但无论如何,这是使用 fetch ^^ 将 base64 转换为 blob 的另一种方法,

var url = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="

fetch(url)
.then(res => res.blob())
.then(blob => {
  var fd = new FormData()
  fd.append('image', blob, 'filename')
  
  console.log(blob)

  // Upload
  // fetch('upload', {method: 'POST', body: fd})
})

什么是 fetch,它有什么关系?
2021-03-11 01:39:54
请记住,并非所有 fetch 实现都普遍支持blob:data:我们使用这种方法,因为我们知道我们只会处理移动浏览器(WebKit),但是例如 Edge,不支持 itt:developer.mozilla.org/en-US/docs/Web/API/...
2021-03-12 01:39:54
Fetch 是一种现代 ajax 方法,您可以使用它来代替XMLHttpRequest数据 url 只是一个 url,您可以使用 ajax 来获取该资源,并且您可以选择将其作为 blob、arraybuffer 或文本
2021-03-21 01:39:54
@Endless 'fetch()' 一个本地 base64 字符串......一个非常聪明的黑客!
2021-03-24 01:39:54