首页 文章

在JavaScript中从base64字符串创建Blob

提问于
浏览
308

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

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

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

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

window.location = blobUrl;

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

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

var dataUrl = 'data:' + contentType + ';base64,' + b64Data;

window.location = dataUrl;

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


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

8 回答

  • 603

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

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

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

    NPM上提供了下述功能:var b64toBlob = require('b64-to-blob')

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

    var byteCharacters = atob(b64Data);
    

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

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

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

    var byteArray = new Uint8Array(byteNumbers);
    

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

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

    上面的代码有效 . 但是,通过在较小的切片中处理 byteCharacters 而不是一次性处理,可以稍微改善性能 . 在我的粗略测试中,512字节似乎是一个很好的切片大小 . 这给了我们以下功能 .

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

    ...或ES6:

    'use strict';
    
    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);
    
  • 2

    无法避免在没有依赖项或库的情况下发布简约方法 .
    它需要新的fetch API . Can I use it?

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

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


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

    // get some dummy gradient image
    var img=function(){var a=document.createElement("canvas"),b=a.getContext("2d"),c=b.createLinearGradient(0,0,200,100);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 = 1000
      console.time('b64')
      while (i--) {
        await b64toBlob(b64Data)
      }
      console.timeEnd('b64')
      
      // bench non blocking
      i = 1000
    
      // 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()
    
  • 51

    对于图像数据,我发现使用 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
        });
    
  • 8

    看这个例子: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);
    
  • 11

    我注意到,在切换像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();
    

    所以我决定在一个函数中包含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;
        }
    }
    
  • 17

    如果你可以为你的项目添加一个依赖项,那就是提供方便的 base64StringToBlob 功能的blob-util npm package . 添加到 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...
    

相关问题