在JavaScript中使用base64string创build一个Blob
我有一个stringbase64编码的二进制数据。
var contentType = 'image/png'; var b64Data = 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==';
我想创build一个包含这些数据的blob:
URL并将其显示给用户。
var blob = new Blob(????, {type: contentType}); var blobUrl = URL.createObjectURL(blob); window.location = blobUrl;
我一直无法弄清楚如何创buildBlob
。
在某些情况下,我可以通过使用data:
URL来避免这种情况。
var dataUrl = 'data:' + contentType + ';base64,' + b64Data; window.location = dataUrl;
但是在大多数情况下, data:
URL非常大。
我怎样才能解码一个base64string到JavaScript中的Blob
对象?
下面介绍的函数在NPM上可用 :
var b64toBlob = require('b64-to-blob')
atob
函数会将base64编码的string解码为一个新string,并为每个二进制数据字节添加一个字符。
var byteCharacters = atob(b64Data);
每个字符的代码点(charCode)将是该字节的值。 我们可以通过为string中的每个字符使用.charCodeAt
方法来创build一个字节值数组。
var byteNumbers = new Array(byteCharacters.length); for (var i = 0; i < byteCharacters.length; i++) { byteNumbers[i] = byteCharacters.charCodeAt(i); }
您可以将这个字节值的数组转换成一个真正的types的字节数组,通过传递给Uint8Array
构造函数。
var byteArray = new Uint8Array(byteNumbers);
这反过来可以转换为一个Blob
通过包装在一个数组传递给Blob
构造函数。
var blob = new Blob([byteArray], {type: contentType});
上面的代码工作。 然而,通过在较小的片上处理byteCharacters
而不是一次性地改进性能可以得到一点改进。 在我粗略的testing中,512字节似乎是一个很好的分片大小。 这给了我们以下的function。
function b64toBlob(b64Data, contentType, sliceSize) { contentType = contentType || ''; sliceSize = sliceSize || 512; var byteCharacters = atob(b64Data); var byteArrays = []; for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) { var slice = byteCharacters.slice(offset, offset + sliceSize); var byteNumbers = new Array(slice.length); for (var i = 0; i < slice.length; i++) { byteNumbers[i] = slice.charCodeAt(i); } var byteArray = new Uint8Array(byteNumbers); byteArrays.push(byteArray); } var blob = new Blob(byteArrays, {type: contentType}); return blob; }
var blob = b64toBlob(b64Data, contentType); var blobUrl = URL.createObjectURL(blob); window.location = blobUrl;
完整的例子:
function b64toBlob(b64Data, contentType, sliceSize) { contentType = contentType || ''; sliceSize = sliceSize || 512; var byteCharacters = atob(b64Data); var byteArrays = []; for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) { var slice = byteCharacters.slice(offset, offset + sliceSize); var byteNumbers = new Array(slice.length); for (var i = 0; i < slice.length; i++) { byteNumbers[i] = slice.charCodeAt(i); } var byteArray = new Uint8Array(byteNumbers); byteArrays.push(byteArray); } var blob = new Blob(byteArrays, {type: contentType}); return blob; } var contentType = 'image/png'; var b64Data = 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='; var blob = b64toBlob(b64Data, contentType); var blobUrl = URL.createObjectURL(blob); var img = document.createElement('img'); img.src = blobUrl; document.body.appendChild(img);
无法避免不发布没有依赖或库的简约方法。
它需要新的获取API。 我可以使用它吗?
var url = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" fetch(url) .then(res => res.blob()) .then(blob => console.log(blob))
优化(但不太可读)的实现:
function base64toBlob(base64Data, contentType) { contentType = contentType || ''; var sliceSize = 1024; var byteCharacters = atob(base64Data); var bytesLength = byteCharacters.length; var slicesCount = Math.ceil(bytesLength / sliceSize); var byteArrays = new Array(slicesCount); for (var sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) { var begin = sliceIndex * sliceSize; var end = Math.min(begin + sliceSize, bytesLength); var bytes = new Array(end - begin); for (var offset = begin, i = 0 ; offset < end; ++i, ++offset) { bytes[i] = byteCharacters[offset].charCodeAt(0); } byteArrays[sliceIndex] = new Uint8Array(bytes); } return new Blob(byteArrays, { type: contentType }); }
对于所有的浏览器支持,特别是在Android上。 也许你可以添加这个
try{ blob = new Blob( byteArrays, {type : contentType}); } catch(e){ // TypeError old chrome and FF window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder; if(e.name == 'TypeError' && window.BlobBuilder){ var bb = new BlobBuilder(); bb.append(byteArrays); blob = bb.getBlob(contentType); } else if(e.name == "InvalidStateError"){ // InvalidStateError (tested on FF13 WinXP) blob = new Blob(byteArrays, {type : contentType}); } else{ // We're screwed, blob constructor unsupported entirely } }
对于图像数据,我发现使用canvas.toBlob
(asynchronous)更简单。
function b64toBlob(b64, onsuccess, onerror) { var img = new Image(); img.onerror = onerror; img.onload = function onload() { var canvas = document.createElement('canvas'); canvas.width = img.width; canvas.height = img.height; var ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0, canvas.width, canvas.height); canvas.toBlob(onsuccess); }; img.src = b64; } var base64Data = 'data:image/jpg;base64,/9j/4AAQSkZJRgABAQA...'; b64toBlob(base64Data, function(blob) { var url = window.URL.createObjectURL(blob); // do something with url }, function(error) { // handle error });
看到这个例子: https : //jsfiddle.net/pqhdce2L/
function b64toBlob(b64Data, contentType, sliceSize) { contentType = contentType || ''; sliceSize = sliceSize || 512; var byteCharacters = atob(b64Data); var byteArrays = []; for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) { var slice = byteCharacters.slice(offset, offset + sliceSize); var byteNumbers = new Array(slice.length); for (var i = 0; i < slice.length; i++) { byteNumbers[i] = slice.charCodeAt(i); } var byteArray = new Uint8Array(byteNumbers); byteArrays.push(byteArray); } var blob = new Blob(byteArrays, {type: contentType}); return blob; } var contentType = 'image/png'; var b64Data = Your Base64 encode; var blob = b64toBlob(b64Data, contentType); var blobUrl = URL.createObjectURL(blob); var img = document.createElement('img'); img.src = blobUrl; document.body.appendChild(img);
我注意到,像Jeremybuild议的那样,切片数据时Internet Explorer 11的速度非常慢。 这对于Chrome来说是正确的,但是在将切片数据传递给Blob构造函数时IE似乎有问题。 在我的机器上,传递5MB的数据使得IE崩溃和内存消耗正在进入屋顶。 Chrome立即创build了这个blob。
运行以下代码进行比较:
var byteArrays = [], megaBytes = 2, byteArray = new Uint8Array(megaBytes*1024*1024), block, blobSlowOnIE, blobFastOnIE, i; for (i = 0; i < (megaBytes*1024); i++) { block = new Uint8Array(1024); byteArrays.push(block); } //debugger; console.profile("No Slices"); blobSlowOnIE = new Blob(byteArrays, { type: 'text/plain' }); console.profileEnd(); console.profile("Slices"); blobFastOnIE = new Blob([byteArray], { type: 'text/plain' }); console.profileEnd();
所以我决定将jeremy描述的两种方法都包含在一个函数中。 学分转到他这个。
function base64toBlob(base64Data, contentType, sliceSize) { var byteCharacters, byteArray, byteNumbers, blobData, blob; contentType = contentType || ''; byteCharacters = atob(base64Data); // Get blob data sliced or not blobData = sliceSize ? getBlobDataSliced() : getBlobDataAtOnce(); blob = new Blob(blobData, { type: contentType }); return blob; /* * Get blob data in one slice. * => Fast in IE on new Blob(...) */ function getBlobDataAtOnce() { byteNumbers = new Array(byteCharacters.length); for (var i = 0; i < byteCharacters.length; i++) { byteNumbers[i] = byteCharacters.charCodeAt(i); } byteArray = new Uint8Array(byteNumbers); return [byteArray]; } /* * Get blob data in multiple slices. * => Slow in IE on new Blob(...) */ function getBlobDataSliced() { var slice, byteArrays = []; for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) { slice = byteCharacters.slice(offset, offset + sliceSize); byteNumbers = new Array(slice.length); for (var i = 0; i < slice.length; i++) { byteNumbers[i] = slice.charCodeAt(i); } byteArray = new Uint8Array(byteNumbers); // Add slice byteArrays.push(byteArray); } return byteArrays; } }