将数据 URI 转换为文件,然后附加到 FormData
Posted
技术标签:
【中文标题】将数据 URI 转换为文件,然后附加到 FormData【英文标题】:Convert Data URI to File then append to FormData 【发布时间】:2011-06-27 07:13:59 【问题描述】:我一直在尝试重新实现一个 html5 图像上传器,例如 on the Mozilla Hacks 网站,但它适用于 WebKit 浏览器。部分任务是从canvas
对象中提取图像文件并将其附加到FormData 对象以进行上传。
问题在于,虽然 canvas
具有 toDataURL
函数来返回图像文件的表示形式,但 FormData 对象仅接受来自 File API 的 File 或 Blob 对象。
Mozilla 解决方案在canvas
上使用了以下仅限 Firefox 的功能:
var file = canvas.mozGetAsFile("foo.png");
...这在 WebKit 浏览器上不可用。我能想到的最佳解决方案是找到某种方法将 Data URI 转换为 File 对象,我认为这可能是 File API 的一部分,但我一辈子都找不到这样做的东西。
有可能吗?如果没有,还有其他选择吗?
谢谢。
【问题讨论】:
如果要将图片的DataURI保存到服务器:***.com/a/50131281/5466401 【参考方案1】:在玩了一些东西之后,我自己设法解决了这个问题。
首先,这会将 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);
【讨论】:
为什么总是会发生这种情况...你试图用 SO 搜索来解决一个问题几个小时。然后你发布一个问题。在一个小时内,您会从另一个问题中得到答案。不是我在抱怨...***.com/questions/9388412/… @stoive 我可以构建 Blob,但您能解释一下如何将POST
或 PUT
构建到 S3 吗?
@mimo - 它指向底层的ArrayBuffer
,然后将其写入BlobBuilder
实例。
@stoive 那为什么不是 bb.append(ia)?
谢谢!这通过一个小的修正解决了我的问题var file = new File( [blob], 'canvasImage.jpg', type: 'image/jpeg' ); fd.append("canvasImage", file);
【参考方案2】:
现在不推荐使用 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');
【讨论】:
只是一个想法:array=[]; array.length=binary.length;
... array[i]=bina
... 等等。所以数组是预先分配的。它节省了每次迭代都必须扩展数组的 push(),而且我们在这里可能处理数百万个项目(=字节),所以这很重要。
在 Safari 上对我来说也失败了。 @WilliamT。的答案适用于 Firefox/Safari/Chrome。
"binary" 是一个有点误导的名字,因为它不是一个位数组,而是一个字节数组。
"type: 'image/jpeg'" - 如果是 png 图像或者如果您事先不知道图像扩展名怎么办?
先创建 Uint8Array 比先创建 Array 然后再转换为 Uint8Array 好。【参考方案3】:
这个适用于 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' );
【讨论】:
太棒了!但是我想你仍然可以保持 mime 字符串动态,就像在 Stoive 的解决方案中一样? // 分离出 mime 组件 var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0] 带有 webkit 前缀的 iOS6 的回退是什么?你是怎么处理的?【参考方案4】: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");
另外,您可以为非壁虎引擎浏览器创建一个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 的 polyfill 是处理这个问题恕我直言的正确方法。 我想强调这篇文章的最后一件事:“现在 canvas.toBlob() 适用于所有现代浏览器。”【参考方案5】:我的首选方式是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 以及它的相关性如何? Fetch 是一种现代 ajax 方法,您可以使用它来代替XMLHttpRequest
,因为数据 url 只是一个 url,您可以使用 ajax 来获取该资源,并且您可以选择是否需要它作为 blob、arraybuffer 或文本
@Endless 'fetch()' 一个本地 base64 字符串...一个非常聪明的 hack!
请记住,blob:
和 data:
并非所有 fetch 实现都普遍支持。我们使用这种方法,因为我们知道我们只会处理移动浏览器 (WebKit),但 Edge 例如不支持它:developer.mozilla.org/en-US/docs/Web/API/…【参考方案6】:
感谢@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);
【讨论】:
【参考方案7】:不断发展的标准看起来是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?
【讨论】:
我使用的是没有 polyfills 的 Chrome,并且不记得遇到过命名空间。我热切期待canvas.toBlob()
- 它似乎比getAsFile
更合适。
BlobBuilder
似乎被Blob
弃用了
BlobBuilder 已被弃用,这种模式很糟糕。更好的是:window.BlobBuilder = (window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder);嵌套的 try catch 真的很难看,如果没有可用的构建器会发生什么?
这有多糟糕?如果抛出异常并且 1) BlobBuilder 不存在,则不会发生任何事情并执行下一个块。 2) 如果它确实存在,但是抛出了一个异常,那么它被弃用并且不应该被使用,所以它继续到下一个 try 块。理想情况下,您会首先检查是否支持 Blob,甚至在此检查 toBlob 支持之前。【参考方案8】:
var BlobBuilder = (window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder);
可以在没有 try catch 的情况下使用。
感谢 check_ca。做得好。
【讨论】:
如果浏览器支持已弃用的 BlobBuilder,这仍然会引发错误。如果浏览器支持旧方法,它将使用旧方法,即使它支持新方法。这是不希望的,请参阅下面 Chris Bosco 的方法【参考方案9】:通过更改最后一行以适应 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);
【讨论】:
【参考方案10】:这是Stoive's answer的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);
用法:
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);
【讨论】:
【参考方案11】:谢谢! @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);
【讨论】:
【参考方案12】:简单点:D
function dataURItoBlob(dataURI,mime)
// convert base64 to raw binary data held in a string
// doesn't handle URLEncoded DataURIs
var byteString = window.atob(dataURI);
// separate out the mime component
// write the bytes of the string to an ArrayBuffer
//var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++)
ia[i] = byteString.charCodeAt(i);
// write the ArrayBuffer to a blob, and you're done
var blob = new Blob([ia], type: mime );
return blob;
【讨论】:
【参考方案13】:toDataURL 为您提供一个字符串,您可以将该字符串放入隐藏的输入中。
【讨论】:
你能举个例子吗?我不想上传 base64 字符串(<input type=hidden value="data:..." />
会这样做),我想上传文件数据(就像 <input type="file" />
所做的那样,除非你不能设置 @987654323 @ 属性)。
这应该是评论而不是答案。请用适当的解释详细说明答案。 @陈猫【参考方案14】:
我遇到了与 Ravinder Payal 完全相同的问题,并且我找到了答案。试试这个:
var dataURL = canvas.toDataURL("image/jpeg");
var name = "image.jpg";
var parseFile = new Parse.File(name, base64: dataURL.substring(23));
【讨论】:
你真的建议使用 Parse.com 吗?你应该提到你的答案需要依赖! WTF ?为什么有人会将图像的base64代码上传到PARSE服务器然后下载?当我们可以直接在我们的服务器上上传base64时,主要是上传base64字符串或图像文件需要相同的数据。如果你只是想让用户下载图像,你可以使用这个window.open(canvas.toDataURL("image/jpeg"))
以上是关于将数据 URI 转换为文件,然后附加到 FormData的主要内容,如果未能解决你的问题,请参考以下文章
Terraform - 创建 EBS 的快照,然后将快照转换为 EBS 并附加到 EC2