在JavaScript中从base64字符串创建Blob

405 javascript base64

我在字符串中有base64编码的二进制数据.

const contentType = 'image/png';
const b64Data = 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==';
Run Code Online (Sandbox Code Playgroud)

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

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

window.location = blobUrl;
Run Code Online (Sandbox Code Playgroud)

我还没有弄清楚如何创建Blob.

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

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

window.location = dataUrl;
Run Code Online (Sandbox Code Playgroud)

但是在大多数情况下,data:URL非常大.


如何Blob在JavaScript中将base64字符串解码为对象?

小智 724

NPM上提供了下述功能:atob

.charCodeAt函数将base64编码的字符串解码为一个新字符串,其中包含二进制数据每个字节的字符.

const byteCharacters = atob(b64Data);
Run Code Online (Sandbox Code Playgroud)

每个字符的代码点(charCode)都是字节的值.我们可以通过使用Uint8Array字符串中每个字符的方法应用它来创建一个字节值数组.

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

您可以将此字节值数组转换为实际类型的字节数组,方法是将其传递给Blob构造函数.

const byteArray = new Uint8Array(byteNumbers);
Run Code Online (Sandbox Code Playgroud)

这反过来可以Blob通过将其包装在一个将其传递给byteCharacters构造函数的数组中转换为a .

const blob = new Blob([byteArray], {type: contentType});
Run Code Online (Sandbox Code Playgroud)

上面的代码有效.然而,通过处理atob较小的切片而不是一次性处理,可以稍微改善性能.在我的粗略测试中,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;
}
Run Code Online (Sandbox Code Playgroud)
const blob = b64toBlob(b64Data, contentType);
const blobUrl = URL.createObjectURL(blob);

window.location = blobUrl;
Run Code Online (Sandbox Code Playgroud)

完整示例:

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);
Run Code Online (Sandbox Code Playgroud)

......或ES6:

const byteCharacters = atob(b64Data);
Run Code Online (Sandbox Code Playgroud)

  • 每当我在 javascript 中找到一些可以做的事情时,我发现它每次都变得更加可怕、可怕、可怕。多么糟糕的黑客精心策划的集合啊 (12认同)
  • 嗨,杰里米.我们已经在我们的Web应用程序中使用了此代码,并且在下载的文件较大之前它不会导致任何问题.因此,当用户使用Chrome或IE下载大于100mb的文件时,它会导致生产服务器出现挂起和崩溃.我们发现在IE后面的行中引发了内存异常"var byteNumbers = new Array(slice.length)".但是在chrome中,for循环导致同样的问题.我们无法找到解决此问题的正确方法,然后我们使用window.open直接下载文件.你能在这里提供一些帮助吗? (4认同)

End*_*ess 221

无法避免在没有依赖项或库的情况下发布简约方法.
它需要新的fetch API.我可以用吗?

var url = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="

fetch(url)
.then(res => res.blob())
.then(console.log)
Run Code Online (Sandbox Code Playgroud)

使用此方法,您还可以轻松获取arraybuffer,text和json


我对Jeremy的es6同步版进行了简单的性能测试.
同步版本将阻止UI一段时间.

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

  • 如果 base64 编码字符串的大小很大,假设大于 665536 个字符,这是 Opera 中 URI 大小的限制,这仍然有效吗? (3认同)
  • 作为内联甚至更好:`await(await fetch(imageDataURL))。blob()` (3认同)
  • 当然,如果您定位到最新的浏览器。但这也要求该函数位于异步函数内。说到...`await fetch(url).then(r =&gt; r.blob())`是排序器 (3认同)
  • 不知道,我知道它可能是对地址栏的一个限制,但使用 AJAX 做事可能是一个例外,因为它不必被渲染。你必须测试它。如果它在我那里,我一开始就不会得到 base64 字符串。认为这是一种不好的做法,会占用更多的内存和时间来解码和编码。例如,`createObjectURL` 而不是 `readAsDataURL` 更好。如果您使用 ajax 上传文件,请选择“FormData”而不是“JSON”,或者使用“canvas.toBlob”而不是“toDataURL” (2认同)
  • 非常简洁的解决方案,但据我所知,由于“拒绝访问”错误,IE(使用 polyfill ofc)无法使用。我猜`fetch` 在blob url 下暴露blob - 与`URL.createObjectUrl` 一样 - 这在ie11 上不起作用。[参考](https://answers.microsoft.com/en-us/ie/forum/all/unable-to-preview-the-pdf-filecontent-using-blob/ba1806e8-307e-4b8f-92d1-b986f5537052) . 也许有一些解决方法可以在 IE11 中使用 fetch?它看起来比其他同步解决方案好得多:) (2认同)

Bac*_*her 67

优化(但不太可读)的实现:

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 });
}
Run Code Online (Sandbox Code Playgroud)

  • 是否有任何理由将字节切成 blob?如果我不使用,是否有任何不利或风险? (3认同)
  • 仅是示例,我可以在IE 11和Chrome的企业环境中无缝处理任何文档类型。 (3认同)
  • 这是太棒了。谢谢你! (2认同)

小智 19

以下是我的 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 });
}
Run Code Online (Sandbox Code Playgroud)

  • 您的“Typescript 代码”代码只有 SINGLE 类型,并且该类型是“any”。就像为什么还要麻烦? (7认同)
  • 虽然此代码片段可能是解决方案,但 [包括解释](//meta.stackexchange.com/questions/114762/explaining-entirely-‌ code-based-answers) 确实有助于提高帖子的质量。请记住,您是在为将来的读者回答问题,而那些人可能不知道您提出代码建议的原因。 (4认同)
  • 另外,你为什么对评论大喊大叫? (2认同)

Jay*_*Lin 18

对于所有浏览器支持,特别是在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   
        }
    }
Run Code Online (Sandbox Code Playgroud)


小智 16

对于像我一样的所有复制粘贴爱好者,这里有一个适用于 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);
    });
}
Run Code Online (Sandbox Code Playgroud)


ami*_*sim 13

对于图像数据,我觉得使用起来比较简单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
    });
Run Code Online (Sandbox Code Playgroud)


Pap*_*api 13

我发布了一种更具声明性的同步 Base64 转换方式。虽然 asyncfetch().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' });
};
Run Code Online (Sandbox Code Playgroud)

奖金

如果您想打印它,您可以执行以下操作:

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;
   }
};
Run Code Online (Sandbox Code Playgroud)

奖励 x 2 - 在 Internet Explorer 11 的新选项卡中打开 BLOB 文件

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


Arc*_*ela 12

看到这个例子: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);
Run Code Online (Sandbox Code Playgroud)


mar*_*oss 9

我注意到,在切换像jeremy建议的数据时,Internet Explorer 11变得异常缓慢.这适用于Chrome,但在将切片数据传递给Blob-Constructor时IE似乎有问题.在我的机器上,传递5 MB的数据会导致IE崩溃和内存消耗.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();
Run Code Online (Sandbox Code Playgroud)

所以我决定在一个函数中包含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;
    }
}
Run Code Online (Sandbox Code Playgroud)


A.C*_*han 9

只需在浏览器中

Uint8Array.from(atob(YOUR_BASE64_DATA), (c) => c.charCodeAt(0))
Run Code Online (Sandbox Code Playgroud)

与之比较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
})();
Run Code Online (Sandbox Code Playgroud)
!(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
})();
Run Code Online (Sandbox Code Playgroud)

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


gab*_*nta 8

如果你能忍受向你的项目添加一个依赖项,那么伟大的blob-utilnpm 包提供了一个方便的base64StringToBlob功能。添加到您的后,package.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...
Run Code Online (Sandbox Code Playgroud)

  • 这个答案对我在 2021 年使用 Angular 12 有用 (2认同)

aks*_*hay 5

带 fetch 的方法是最好的解决方案,但如果有人需要使用不带 fetch 的方法,那么就在这里,因为前面提到的方法对我不起作用:

function makeblob(dataURL) {
    const BASE64_MARKER = ';base64,';
    const parts = dataURL.split(BASE64_MARKER);
    const contentType = parts[0].split(':')[1];
    const raw = window.atob(parts[1]);
    const rawLength = raw.length;
    const uInt8Array = new Uint8Array(rawLength);

    for (let i = 0; i < rawLength; ++i) {
        uInt8Array[i] = raw.charCodeAt(i);
    }

    return new Blob([uInt8Array], { type: contentType });
}
Run Code Online (Sandbox Code Playgroud)