我有一个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?


当前回答

我将发布一种更声明性的同步Base64转换方式。虽然async fetch().blob()非常整洁,我非常喜欢这个解决方案,但它在Internet Explorer 11(可能还有Edge -我还没有测试过这个)上不起作用,即使是在polyfill上-看看我对Endless的帖子的评论,了解更多细节。

const blobPdfFromBase64String = base64String => {
   const byteArray = Uint8Array.from(
     atob(base64String)
       .split('')
       .map(char => char.charCodeAt(0))
   );
  return new Blob([byteArray], { type: 'application/pdf' });
};

奖金

如果你想打印它,你可以这样做:

const isIE11 = !!(window.navigator && window.navigator.msSaveOrOpenBlob); // Or however you want to check it
const printPDF = blob => {
   try {
     isIE11
       ? window.navigator.msSaveOrOpenBlob(blob, 'documents.pdf')
       : printJS(URL.createObjectURL(blob)); // http://printjs.crabbly.com/
   } catch (e) {
     throw PDFError;
   }
};

奖金x 2 -打开一个BLOB文件在新选项卡为Internet Explorer 11

如果你能够在服务器上对Base64字符串做一些预处理,你可以在一些URL下公开它,并使用printJS中的链接:)

其他回答

在浏览器中

Uint8Array.from(atob(YOUR_BASE64_DATA), (c) => c.charCodeAt(0))

与fetch比较

!(async () => {
  const start = performance.now();
  let i = 0;
  while (i++ < 1e3) {
    const dataUrl =
      "data:application/octet-stream;base64,H4sIAAAAAAAAA0vOzyvOz0nVy8lP10jISM3JyVdIr8osUFCpdkksSdXLyy/X0KxN0ORKHlU3qm5U3ai6UXWj6kauOgBVt1KRLwcAAA==";
    body = await (await fetch(dataUrl)).blob();
  }
  console.log(performance.now() - start); // 508.19999999925494ms
})();
!(async () => {
  const start = performance.now();
  let i = 0;
  while (i++ < 1e3) {
    const base64Data =
      "H4sIAAAAAAAAA0vOzyvOz0nVy8lP10jISM3JyVdIr8osUFCpdkksSdXLyy/X0KxN0ORKHlU3qm5U3ai6UXWj6kauOgBVt1KRLwcAAA==";
    body = Uint8Array.from(atob(base64Data), (c) => c.charCodeAt(0));
  }
  console.log(performance.now() - start); // 7.899999998509884ms
})();

根据您的数据大小,选择性能一种。

两种不同的变化

function base64ToBlob(base64, contentType='image/png', chunkLength=512) {
    const byteCharsArray = Array.from(atob(base64.substr(base64.indexOf(',') + 1)));
    const chunksIterator = new Array(Math.ceil(byteCharsArray.length / chunkLength));
    const bytesArrays = [];

    for (let c = 0; c < chunksIterator.length; c++) {
        bytesArrays.push(new Uint8Array(byteCharsArray.slice(c * chunkLength, chunkLength * (c + 1)).map(s => s.charCodeAt(0))));
    }

    const blob = new Blob(bytesArrays, {type: contentType});
    
    return blob;
}

/* Not sure how it performs with big images */
async function base64ToBlobLight(base64) { return await fetch(base64).then(res => res.blob()); }

/* Test */
const base64Data = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAB+FBMVEUAAAA/mUPidDHiLi5Cn0XkNTPmeUrkdUg/m0Q0pEfcpSbwaVdKskg+lUP4zA/iLi3msSHkOjVAmETdJSjtYFE/lkPnRj3sWUs8kkLeqCVIq0fxvhXqUkbVmSjwa1n1yBLepyX1xxP0xRXqUkboST9KukpHpUbuvRrzrhF/ljbwaljuZFM4jELaoSdLtElJrUj1xxP6zwzfqSU4i0HYnydMtUlIqUfywxb60AxZqEXaoifgMCXptR9MtklHpEY2iUHWnSjvvRr70QujkC+pUC/90glMuEnlOjVMt0j70QriLS1LtEnnRj3qUUXfIidOjsxAhcZFo0bjNDH0xxNLr0dIrUdmntVTkMoyfL8jcLBRuErhJyrgKyb4zA/5zg3tYFBBmUTmQTnhMinruBzvvhnxwxZ/st+Ktt5zp9hqota2vtK6y9FemNBblc9HiMiTtMbFtsM6gcPV2r6dwroseLrMrbQrdLGdyKoobKbo3Zh+ynrgVllZulTsXE3rV0pIqUf42UVUo0JyjEHoS0HmsiHRGR/lmRz/1hjqnxjvpRWfwtOhusaz0LRGf7FEfbDVmqHXlJeW0pbXq5bec3fX0nTnzmuJuWvhoFFhm0FtrziBsjaAaDCYWC+uSi6jQS3FsSfLJiTirCOkuCG1KiG+wSC+GBvgyhTszQ64Z77KAAAARXRSTlMAIQRDLyUgCwsE6ebm5ubg2dLR0byXl4FDQzU1NDEuLSUgC+vr6urq6ubb29vb2tra2tG8vLu7u7uXl5eXgYGBgYGBLiUALabIAAABsElEQVQoz12S9VPjQBxHt8VaOA6HE+AOzv1wd7pJk5I2adpCC7RUcHd3d3fXf5PvLkxheD++z+yb7GSRlwD/+Hj/APQCZWxM5M+goF+RMbHK594v+tPoiN1uHxkt+xzt9+R9wnRTZZQpXQ0T5uP1IQxToyOAZiQu5HEpjeA4SWIoksRxNiGC1tRZJ4LNxgHgnU5nJZBDvuDdl8lzQRBsQ+s9PZt7s7Pz8wsL39/DkIfZ4xlB2Gqsq62ta9oxVlVrNZpihFRpGO9fzQw1ms0NDWZz07iGkJmIFH8xxkc3a/WWlubmFkv9AB2SEpDvKxbjidN2faseaNV3zoHXvv7wMODJdkOHAegweAfFPx4G67KluxzottCU9n8CUqXzcIQdXOytAHqXxomvykhEKN9EFutG22p//0rbNvHVxiJywa8yS2KDfV1dfbu31H8jF1RHiTKtWYeHxUvq3bn0pyjCRaiRU6aDO+gb3aEfEeVNsDgm8zzLy9egPa7Qt8TSJdwhjplk06HH43ZNJ3s91KKCHQ5x4sw1fRGYDZ0n1L4FKb9/BP5JLYxToheoFCVxz57PPS8UhhEpLBVeAAAAAElFTkSuQmCC';
        
const blob = base64ToBlob(base64Data);
const blobUrl = URL.createObjectURL(blob);
const img = document.createElement('img');

img.src = blobUrl;
document.body.appendChild(img);

/**********************/

/* Test */
(async () => {
    const blob = await base64ToBlobLight(base64Data);
    const blobUrl = URL.createObjectURL(blob);
    const img = document.createElement('img');

    img.src = blobUrl;
    document.body.appendChild(img);
})();

对于图像数据,我发现使用canvas更简单。toBlob(异步)

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
    });

以下是我的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 });
}

atob函数将base64编码的字符串解码为一个新的字符串,每个字节对应一个字符。

const byteCharacters = atob(b64Data);

每个字符的编码点(charCode)将是字节的值。我们可以为字符串中的每个字符使用. charcodeat方法来创建一个字节值数组。

const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
    byteNumbers[i] = byteCharacters.charCodeAt(i);
}

你可以通过传递给Uint8Array构造函数,将这个字节值数组转换为一个真正类型的字节数组。

const byteArray = new Uint8Array(byteNumbers);

通过将其包装在数组中并将其传递给BLOB构造函数,可以将其转换为BLOB。

const blob = new Blob([byteArray], {type: contentType});

上面的代码可以工作。但是,通过将bytecharacter分块处理,而不是一次性处理,可以稍微提高性能。在我的粗略测试中,512字节似乎是一个很好的切片大小。这就得到了下面的函数。

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;
}
const blob = b64toBlob(b64Data, contentType);
const blobUrl = URL.createObjectURL(blob);

window.location = blobUrl;

完整的例子:

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; } const contentType = 'image/png'; const b64Data = 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='; const blob = b64toBlob(b64Data, contentType); const blobUrl = URL.createObjectURL(blob); const img = document.createElement('img'); img.src = blobUrl; document.body.appendChild(img);