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


当前回答

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

其他回答

两种不同的变化

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

这将被证明是一个很短的解决办法。

const byteArray = new Buffer(base64String.replace(/^[\w\d;:\/]+base64\,/g, ''), 'base64');

base64String是包含以64为基数的字符串。

byteArray是你需要的数组。

regex替换是可选的,只是用于处理dataurl字符串中的前缀。

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

对于所有浏览器的支持,特别是在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
    }
}