Creating a BLOB from a Base64 string in JavaScript

后端 未结 12 2628
说谎
说谎 2020-11-21 04:24

I have Base64-encoded binary data in a string:

const contentType = \'image/png\';
const b64Data = \'iVBORw0KGg         


        
相关标签:
12条回答
  • 2020-11-21 05:05

    For all copy-paste lovers out there like me, here is a cooked download function which works on Chrome, Firefox and 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);
        });
    }
    
    0 讨论(0)
  • 2020-11-21 05:06

    Here is a more minimal method without any dependencies or libraries.
    It requires the new fetch API. (Can I use it?)

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

    With this method you can also easily get a ReadableStream, ArrayBuffer, text, and JSON.
    (fyi this also works with node-fetch in Node)

    As a function:

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

    I did a simple performance test towards Jeremy's ES6 sync version.
    The sync version will block UI for a while. keeping the devtool open can slow the fetch performance

    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('b64')
      while (i--) {
        await b64toBlob(b64Data)
      }
      console.timeEnd('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()

    0 讨论(0)
  • 2020-11-21 05:06

    I'm posting a more declarative way of sync Base64 converting. While async fetch().blob() is very neat and I like this solution a lot, it doesn't work on Internet Explorer 11 (and probably Edge - I haven't tested this one), even with the polyfill - take a look at my comment to Endless' post for more details.

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

    Bonus

    If you want to print it you could do something like:

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

    Bonus x 2 - Opening a BLOB file in new tab for Internet Explorer 11

    If you're able to do some preprocessing of the Base64 string on the server you could expose it under some URL and use the link in printJS :)

    0 讨论(0)
  • 2020-11-21 05:07

    If you can stand adding one dependency to your project there's the great blob-util npm package that provides a handy base64StringToBlob function. Once added to your package.json you can use it like this:

    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...
    
    0 讨论(0)
  • 2020-11-21 05:08

    I noticed that Internet Explorer 11 gets incredibly slow when slicing the data like Jeremy suggested. This is true for Chrome, but Internet Explorer seems to have a problem when passing the sliced data to the Blob-Constructor. On my machine, passing 5 MB of data makes Internet Explorer crash and memory consumption is going through the roof. Chrome creates the blob in no time.

    Run this code for a comparison:

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

    So I decided to include both methods described by Jeremy in one function. Credits go to him for this.

    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;
        }
    }
    
    0 讨论(0)
  • 2020-11-21 05:12

    See this example: 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);

    0 讨论(0)
提交回复
热议问题