我有一个base64编码的二进制数据字符串:

const contentType = 'image/png';
const b64Data = 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==';

我想创建一个包含此数据的blob: URL,并将其显示给用户:

const blob = new Blob(????, {type: contentType});
const blobUrl = URL.createObjectURL(blob);

window.location = blobUrl;

我一直没能弄清楚如何创建BLOB。

在某些情况下,我可以通过使用data: URL来避免这种情况:

const dataUrl = `data:${contentType};base64,${b64Data}`;

window.location = dataUrl;

然而,在大多数情况下,数据:url非常大。


我如何解码一个Base64字符串到一个BLOB对象在JavaScript?


当前回答

以下是我的TypeScript代码,可以很容易地转换成JavaScript,你可以使用

/**
 * Convert BASE64 to BLOB
 * @param base64Image Pass Base64 image data to convert into the BLOB
 */
private convertBase64ToBlob(base64Image: string) {
  // Split into two parts
  const parts = base64Image.split(';base64,');

  // Hold the content type
  const imageType = parts[0].split(':')[1];

  // Decode Base64 string
  const decodedData = window.atob(parts[1]);

  // Create UNIT8ARRAY of size same as row data length
  const uInt8Array = new Uint8Array(decodedData.length);

  // Insert all character code into uInt8Array
  for (let i = 0; i < decodedData.length; ++i) {
    uInt8Array[i] = decodedData.charCodeAt(i);
  }

  // Return BLOB image after conversion
  return new Blob([uInt8Array], { type: imageType });
}

其他回答

对于所有像我一样喜欢复制粘贴的人来说,这里有一个可以在Chrome、Firefox和Edge上运行的现成下载功能:

window.saveFile = function (bytesBase64, mimeType, fileName) {
var fileUrl = "data:" + mimeType + ";base64," + bytesBase64;
fetch(fileUrl)
    .then(response => response.blob())
    .then(blob => {
        var link = window.document.createElement("a");
        link.href = window.URL.createObjectURL(blob, { type: mimeType });
        link.download = fileName;
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
    });
}

我注意到,当像Jeremy建议的那样对数据进行切片时,Internet Explorer 11变得异常缓慢。这对于Chrome是正确的,但是Internet Explorer在将切片数据传递给blob -构造函数时似乎有一个问题。在我的机器上,传递5 MB的数据会导致Internet Explorer崩溃,内存消耗也会飙升。Chrome创建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 Internet Explorer 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 Internet Explorer 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;
    }
}

如果你能忍受在你的项目中添加一个依赖,有一个很棒的blob-util npm包,它提供了一个方便的base64StringToBlob函数。一旦添加到您的包。Json,你可以这样使用它:

import { base64StringToBlob } from 'blob-util';

const contentType = 'image/png';
const b64Data = 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==';

const blob = base64StringToBlob(b64Data, contentType);

// Do whatever you need with your blob...

下面是一个更简单的方法,没有任何依赖项或库。 它需要新的获取API。(我可以用吗?)

var url =" data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" fetch (url) .then(res => res.blob()) 不要犹豫(console.log)

使用此方法,您还可以轻松获得ReadableStream、ArrayBuffer、text和JSON。 (供参考,这也适用于节点获取在节点)

作为函数:

const b64toBlob = (base64, type = 'application/octet-stream') => 
  fetch(`data:${type};base64,${base64}`).then(res => res.blob())

但是我建议你一开始就不要使用base64。有更好的方法来发送和接收二进制数据。JSON并不总是最好的选择。它占用更多的带宽和浪费处理时间(解码)的东西。我们,帆布。toBlob而不是canvas。使用FormData发送二进制文件。您还可以返回一个多部分有效负载,并使用来自服务器响应的await response. formdata()对其进行解码。FormData可以是双向的。


我对Jeremy的ES6同步版本做了一个简单的性能测试。 同步版本会阻塞UI一段时间。 保持devtool打开会降低读取性能

document.body.innerHTML += '<input autofocus placeholder="try writing">' // get some dummy gradient image var img=function(){var a=document.createElement("canvas"),b=a.getContext("2d"),c=b.createLinearGradient(0,0,1500,1500);a.width=a.height=3000;c.addColorStop(0,"red");c.addColorStop(1,"blue");b.fillStyle=c;b.fillRect(0,0,a.width,a.height);return a.toDataURL()}(); async function perf() { const blob = await fetch(img).then(res => res.blob()) // turn it to a dataURI const url = img const b64Data = url.split(',')[1] // Jeremy Banks solution const b64toBlob = (b64Data, contentType = '', sliceSize=512) => { const byteCharacters = atob(b64Data); const byteArrays = []; for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) { const slice = byteCharacters.slice(offset, offset + sliceSize); const byteNumbers = new Array(slice.length); for (let i = 0; i < slice.length; i++) { byteNumbers[i] = slice.charCodeAt(i); } const byteArray = new Uint8Array(byteNumbers); byteArrays.push(byteArray); } const blob = new Blob(byteArrays, {type: contentType}); return blob; } // bench blocking method let i = 500 console.time('blocking b64') while (i--) { await b64toBlob(b64Data) } console.timeEnd('blocking b64') // bench non blocking i = 500 // so that the function is not reconstructed each time const toBlob = res => res.blob() console.time('fetch') while (i--) { await fetch(url).then(toBlob) } console.timeEnd('fetch') console.log('done') } perf()

对于所有浏览器的支持,特别是在Android上,也许你可以添加这个:

try{
    blob = new Blob(byteArrays, {type : contentType});
}
catch(e){
    // TypeError old Google Chrome and Firefox
    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
    }
}