将数据URI转换为File然后附加到FormData

Sto*_*ive 270 javascript html5 webkit

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

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

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

var file = canvas.mozGetAsFile("foo.png");
Run Code Online (Sandbox Code Playgroud)

...在WebKit浏览器上不可用.我能想到的最好的解决方案是找到一种方法将数据URI转换为File对象,我认为它可能是File API的一部分,但我不能为我的生活找到一些东西.

可能吗?如果没有,任何替代方案?

谢谢.

Sto*_*ive 455

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

首先,这会将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});
}
Run Code Online (Sandbox Code Playgroud)

从那里,将数据附加到表单以便将其作为文件上载很容易:

var dataURL = canvas.toDataURL('image/jpeg', 0.5);
var blob = dataURItoBlob(dataURL);
var fd = new FormData(document.forms[0]);
fd.append("canvasImage", blob);
Run Code Online (Sandbox Code Playgroud)

  • 为什么总会发生这种情况......你试图用SO搜索来解决几个小时的问题.然后你发一个问题.在一个小时内,您将从另一个问题得到答案.不是我抱怨... http://stackoverflow.com/questions/9388412/data-uri-to-object-url-with-createobjecturl-in-chrome-ff (27认同)
  • 谢谢!这通过一个小的更正解决了我的问题 `var file = new File( [blob], 'canvasImage.jpg', { type: 'image/jpeg' } ); fd.append("canvasImage", 文件); ` (6认同)
  • @stoive在那种情况下为什么它不是bb.append(ia)? (2认同)

小智 141

现在不推荐使用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'});
}
Run Code Online (Sandbox Code Playgroud)

  • 只是一个想法:`array = []; array.length = binary.length;`...`array [i] = bina` ...等所以数组是预先分配的.它保存了每次迭代都必须扩展数组的push(),我们在这里处理数百万个项目(=字节),所以这很重要. (2认同)
  • 在Safari上我也失败了.@WilliamT.但是,它的答案适用于Firefox/Safari/Chrome. (2认同)
  • “type: 'image/jpeg'” - 如果它是 png 图像或者您事先不知道图像扩展名怎么办? (2认同)

Wil*_* T. 52

这个适用于iOS和Safari.

你需要使用Stoive的ArrayBuffer解决方案,但你不能像vava720那样使用BlobBuilder,所以这里是两者的混搭.

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' });
}
Run Code Online (Sandbox Code Playgroud)

  • 大!但是你可以保持mime字符串的动态,比如在Stoive的解决方案中,我想?//分离出mime组件var mimeString = dataURI.split(',')[0] .split(':')[1] .split(';')[0] (11认同)

cui*_*ing 25

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});
}
Run Code Online (Sandbox Code Playgroud)

将此函数与FormData一起使用来处理画布或dataurl.

例如:

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

此外,您可以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);
    };
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • 我想强调这篇文章的最后一件事:“现在canvas.toBlob()适用于所有现代浏览器。” (2认同)

End*_*ess 22

我首选的方法是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})
})
Run Code Online (Sandbox Code Playgroud)


Mim*_*imo 19

感谢@Stoive和@ vava720,我以这种方式结合了两者,避免使用已弃用的BlobBuilder和ArrayBuffer

function dataURItoBlob(dataURI) {
    'use strict'
    var byteString, 
        mimestring 

    if(dataURI.split(',')[0].indexOf('base64') !== -1 ) {
        byteString = atob(dataURI.split(',')[1])
    } else {
        byteString = decodeURI(dataURI.split(',')[1])
    }

    mimestring = dataURI.split(',')[0].split(':')[1].split(';')[0]

    var content = new Array();
    for (var i = 0; i < byteString.length; i++) {
        content[i] = byteString.charCodeAt(i)
    }

    return new Blob([new Uint8Array(content)], {type: mimestring});
}
Run Code Online (Sandbox Code Playgroud)


Chr*_*sco 12

不断发展的标准看起来像是canvas.toBlob()而不是像Mozilla一样猜测的canvas.getAsFile().

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

谢谢你这个伟大的线程!

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

    var bb;
    try {
        bb = new BlobBuilder();
    } catch(e) {
        try {
            bb = new WebKitBlobBuilder();
        } catch(e) {
            bb = new MozBlobBuilder();
        }
    }
Run Code Online (Sandbox Code Playgroud)

你在BlobBuilder上使用另一个库的polyfill吗?


vek*_*dyb 6

这是Stoive 答案的 ES6 版本:

export class ImageDataConverter {
  constructor(dataURI) {
    this.dataURI = dataURI;
  }

  getByteString() {
    let byteString;
    if (this.dataURI.split(',')[0].indexOf('base64') >= 0) {
      byteString = atob(this.dataURI.split(',')[1]);
    } else {
      byteString = decodeURI(this.dataURI.split(',')[1]);
    }
    return byteString;
  }

  getMimeString() {
    return this.dataURI.split(',')[0].split(':')[1].split(';')[0];
  }

  convertToTypedArray() {
    let byteString = this.getByteString();
    let ia = new Uint8Array(byteString.length);
    for (let i = 0; i < byteString.length; i++) {
      ia[i] = byteString.charCodeAt(i);
    }
    return ia;
  }

  dataURItoBlob() {
    let mimeString = this.getMimeString();
    let intArray = this.convertToTypedArray();
    return new Blob([intArray], {type: mimeString});
  }
}
Run Code Online (Sandbox Code Playgroud)

用法:

const dataURL = canvas.toDataURL('image/jpeg', 0.5);
const blob = new ImageDataConverter(dataURL).dataURItoBlob();
let fd = new FormData(document.forms[0]);
fd.append("canvasImage", blob);
Run Code Online (Sandbox Code Playgroud)


Naf*_*mad 5

var BlobBuilder = (window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder);
Run Code Online (Sandbox Code Playgroud)

可以在没有try catch的情况下使用.

感谢check_ca.做得好.


top*_*ara 5

通过更改最后一行以适应 Blob,可以轻松修复 Stoive 的原始答案:

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});
}
Run Code Online (Sandbox Code Playgroud)


wil*_*ola 5

谢谢!@steovi 这个解决方案。

我添加了对 ES6 版本的支持,并从 unescape 更改为 dataURI(unescape 已弃用)。

converterDataURItoBlob(dataURI) {
    let byteString;
    let mimeString;
    let ia;

    if (dataURI.split(',')[0].indexOf('base64') >= 0) {
      byteString = atob(dataURI.split(',')[1]);
    } else {
      byteString = encodeURI(dataURI.split(',')[1]);
    }
    // separate out the mime component
    mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];

    // write the bytes of the string to a typed array
    ia = new Uint8Array(byteString.length);
    for (var i = 0; i < byteString.length; i++) {
      ia[i] = byteString.charCodeAt(i);
    }
    return new Blob([ia], {type:mimeString});
}
Run Code Online (Sandbox Code Playgroud)