Creating a BLOB from a Base64 string in JavaScript

后端 未结 12 2541
说谎
说谎 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 04:48

    Following is my TypeScript code which can be converted easily into JavaScript and you can use

    /**
     * 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 });
    }
    
    0 讨论(0)
  • 2020-11-21 04:53

    Optimized (but less readable) implementation:

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

    The atob function will decode a Base64-encoded string into a new string with a character for each byte of the binary data.

    const byteCharacters = atob(b64Data);
    

    Each character's code point (charCode) will be the value of the byte. We can create an array of byte values by applying this using the .charCodeAt method for each character in the string.

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

    You can convert this array of byte values into a real typed byte array by passing it to the Uint8Array constructor.

    const byteArray = new Uint8Array(byteNumbers);
    

    This in turn can be converted to a BLOB by wrapping it in an array and passing it to the Blob constructor.

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

    The code above works. However the performance can be improved a little by processing the byteCharacters in smaller slices, rather than all at once. In my rough testing 512 bytes seems to be a good slice size. This gives us the following function.

    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;
    

    Full Example:

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

    0 讨论(0)
  • 2020-11-21 04:54

    For image data, I find it simpler to use canvas.toBlob (asynchronous)

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

    The method with fetch is the best solution, but if anyone needs to use a method without fetch then here it is, as the ones mentioned previously didn't work for me:

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

    For all browser support, especially on Android, perhaps you can add this:

    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
        }
    }
    
    0 讨论(0)
提交回复
热议问题