Criando um Blob de uma string base64 em JavaScript

Eu tenho dados binários codificados em base64 em uma string.

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

Eu gostaria de criar um blob: URL contendo esses dados e exibi-los ao usuário.

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

Eu não fui capaz de descobrir como criar o Blob .

Em alguns casos, posso evitar isso usando um URL de data: vez disso.

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

No entanto, na maioria dos casos, os data: URLs são proibitivamente grandes.


Como posso decodificar uma string base64 para um object Blob em JavaScript?

    A function descrita abaixo está disponível no NPM : var b64toBlob = require('b64-to-blob')

    A function atob decodificará uma string codificada em base64 em uma nova string com um caractere para cada byte dos dados binários.

     var byteCharacters = atob(b64Data); 

    O ponto de código de cada personagem (charCode) será o valor do byte. Podemos criar uma matriz de valores de byte aplicando isso usando o método .charCodeAt para cada caractere na string.

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

    Você pode converter essa matriz de valores de bytes em uma matriz de bytes typescripts reais passando-os para o construtor Uint8Array .

     var byteArray = new Uint8Array(byteNumbers); 

    Isso, por sua vez, pode ser convertido em um Blob , agrupando-o em um array, passando-o para o construtor de Blob .

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

    O código acima funciona. No entanto, o desempenho pode ser melhorado um pouco, processando os byteCharacters em fatias menores, em vez de todos de uma vez. No meu teste duro, 512 bytes parecem ser um bom tamanho de fatia. Isso nos dá a seguinte function.

     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; 

    Exemplo Completo:

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

    Não foi possível evitar postar o método minimalista sem dependência nem bibliotecas.
    Requer a nova API de busca. Posso usá-lo?

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

    Implementação otimizada (mas menos legível):

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

    Para todo o suporte do navegador, especialmente no android. Talvez você possa adicionar isso

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

    Para dados de imagem, acho mais simples usar 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 }); 

    Veja este exemplo: 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); 

    Notei que o Internet Explorer 11 fica incrivelmente lento ao cortar dados como Jeremy sugeriu. Isso vale para o Chrome, mas o IE parece ter um problema ao passar os dados fatiados para o Blob-Constructor. Na minha máquina, passar 5 MB de dados faz com que o IE falhe e o consumo de memory esteja passando do limite. O Chrome cria o blob em pouco tempo.

    Execute este código para um comparativo:

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

    Então decidi include os dois methods descritos por Jeremy em uma function. Créditos vão para ele por isso.

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

    Se você conseguir adicionar uma dependência ao seu projeto, há o ótimo pacote npm blob-util que fornece uma function base64StringToBlob . Uma vez adicionado ao seu package.json você pode usá-lo assim:

     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...