Converter string base64 em ArrayBuffer

96

Eu preciso converter uma string de codificação base64 em um ArrayBuffer. As strings de base64 são entradas do usuário, elas serão copiadas e coladas de um e-mail, portanto, não estarão lá quando a página for carregada. Eu gostaria de fazer isso em javascript sem fazer uma chamada ajax para o servidor, se possível.

Achei esses links interessantes, mas eles não me ajudaram:

ArrayBuffer para string codificada em base64

trata-se da conversão oposta, de ArrayBuffer para base64, não o contrário

http://jsperf.com/json-vs-base64/2

parece bom, mas não consigo descobrir como usar o código.

Existe uma maneira fácil (talvez nativa) de fazer a conversão? obrigado

Tony
fonte

Respostas:

144

Experimente isto:

function _base64ToArrayBuffer(base64) {
    var binary_string = window.atob(base64);
    var len = binary_string.length;
    var bytes = new Uint8Array(len);
    for (var i = 0; i < len; i++) {
        bytes[i] = binary_string.charCodeAt(i);
    }
    return bytes.buffer;
}
Goran.it
fonte
3
Por favor, me explique o que realmente está acontecendo aqui.
Govinda Sakhare
4
Bem, é bastante simples, primeiro decodificamos a string base64 (atob), depois criamos um novo array de inteiros não assinados de 8 bits com o mesmo comprimento da string decodificada. Depois disso, iteramos a string e preenchemos a matriz com o valor Unicode de cada caractere da string.
Goran.it
2
Do MDN: Base64 é um grupo de esquemas de codificação binário para texto semelhantes que representam dados binários em um formato de string ASCII, convertendo-os em uma representação de base-64. A matriz digitada Uint8Array representa uma matriz de inteiros sem sinal de 8 bits, e estamos trabalhando com representação ASCII dos dados (que também é uma tabela de 8 bits) ..
Goran.it
3
Isso não está correto. Ele permite que o javascript interprete os bytes como strings, o que afeta os dados que são realmente binários.
Tomáš Zato - Reintegração de Monica
4
o problema é que a) nem toda sequência de bytes é unicode válida b) nem todo caractere em Unicode tem um byte, então bytes[i] = binary_string.charCodeAt(i);pode estar errado
mistura
51

Usando TypedArray.from :

Uint8Array.from(atob(base64_string), c => c.charCodeAt(0))

Desempenho a ser comparado com a versão for loop da resposta de Goran.it.

Ofavre
fonte
2
Para quem gosta desse tipo de liner, lembre-se que Uint8Array.fromainda tem pouca compatibilidade com alguns navegadores.
IzumiSy
2
Não recomende atob ou btoa: developer.mozilla.org/en-US/docs/Web/API/WindowBase64/…
Kugel
o compilador do rails não consegue lidar com essa string e falha com ExecJS::RuntimeError: SyntaxError: Unexpected token: operator (>); (trilhos 5)
Avael Kross
3
Este não é um buffer de array. Esta é a matriz digitada. Você obtém acesso ao buffer da matriz por meio da .bufferpropriedade do que é retornado deUint8Array
oligofren
4
@Saites, Não há nada de errado com atobou btoa, você apenas precisa fornecer uma entrada válida. atobprecisa de uma string base64 válida, caso contrário, ele gerará um erro. E btoaprecisa de uma string de byte válida (também chamada de string binária), que é uma string contendo caracteres no intervalo 0-255. Se sua string tiver caracteres fora desse intervalo, btoaocorrerá um erro.
GetFree
33

A resposta de Goran.it não funciona devido a um problema de unicode em javascript - https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding .

Acabei usando a função fornecida no blog de Daniel Guerrero: http://blog.danguer.com/2011/10/24/base64-binary-decoding-in-javascript/

A função está listada no link do github: https://github.com/danguer/blog-examples/blob/master/js/base64-binary.js

Use estas linhas

var uintArray = Base64Binary.decode(base64_string);  
var byteArray = Base64Binary.decodeArrayBuffer(base64_string); 
Yaan
fonte
1
Este método é 2x mais rápido do que usar Atob.
xiaoyu2er
4
Você pode dar um exemplo em que não funcionaria? O artigo fala sobre a codificação de strings arbitrárias, que podem conter caracteres Unicode, mas não se aplicam a atobtodos.
riv
1
decodeArrayBufferretorna um ArrayBufferque tem tamanho sempre divisível por 3, que não entendo se é por design ou um bug. Vou perguntar no projeto github.
ceztko
@ceztko Provavelmente é por design (acidental). O algoritmo de codificação base64 pega grupos de 3 bytes e os transforma em 4 caracteres. O método de decodificação provavelmente aloca um ArrayBuffer cujo comprimento é base64String.length / 4 * 3 bytes e nunca trunca quaisquer bytes não utilizados ao terminar.
AlwaysLearning
1
@AlwaysLearning, o que significa que provavelmente está bugado, uma vez que zero bytes restantes podem corromper o conteúdo de saída pretendido.
ceztko de
21

Acabei de encontrar base64-arraybuffer, um pequeno pacote npm com uso incrivelmente alto, 5 milhões de downloads no mês passado (2017-08).

https://www.npmjs.com/package/base64-arraybuffer

Para quem procura a melhor solução padrão, pode ser esta.

jv-dev
fonte
9

Solução assíncrona , é melhor quando os dados são grandes:

// base64 to buffer
function base64ToBufferAsync(base64) {
  var dataUrl = "data:application/octet-binary;base64," + base64;

  fetch(dataUrl)
    .then(res => res.arrayBuffer())
    .then(buffer => {
      console.log("base64 to buffer: " + new Uint8Array(buffer));
    })
}

// buffer to base64
function bufferToBase64Async( buffer ) {
    var blob = new Blob([buffer], {type:'application/octet-binary'});    
    console.log("buffer to blob:" + blob)

    var fileReader = new FileReader();
    fileReader.onload = function() {
      var dataUrl = fileReader.result;
      console.log("blob to dataUrl: " + dataUrl);

      var base64 = dataUrl.substr(dataUrl.indexOf(',')+1)      
      console.log("dataUrl to base64: " + base64);
    };
    fileReader.readAsDataURL(blob);
}
张浩然
fonte
6

Javascript é um bom ambiente de desenvolvimento, então parece estranho que ele não forneça uma solução para este pequeno problema. As soluções oferecidas em outras partes desta página são potencialmente lentas. Aqui está minha solução. Ele emprega a funcionalidade embutida que decodifica urls de dados de imagem e som em base64.

var req = new XMLHttpRequest;
req.open('GET', "data:application/octet;base64," + base64Data);
req.responseType = 'arraybuffer';
req.onload = function fileLoaded(e)
{
   var byteArray = new Int8Array(e.target.response);
   // var shortArray = new Int16Array(e.target.response);
   // var unsignedShortArray = new Int16Array(e.target.response);
   // etc.
}
req.send();

A solicitação de envio falha se a string de base 65 estiver mal formada.

O tipo MIME (aplicativo / octeto) é provavelmente desnecessário.

Testado em cromo. Deve funcionar em outros navegadores.

trevo de dinossauro
fonte
1
Esta foi a solução perfeita para mim, simples e limpa. Eu testei rapidamente no Firefox, IE 11, Edge e funcionou bem!
cs-NET
não se relaciona com a pergunta original
James Newton
Não tenho certeza de como isso funciona para você no IE11, mas recebo um Access Deniederro, que parece ser uma limitação do CORS.
Sergiu
6

Para usuários Node.js:

const myBuffer = Buffer.from(someBase64String, 'base64');

myBuffer será do tipo Buffer, que é uma subclasse de Uint8Array. Infelizmente, Uint8Array NÃO é um ArrayBuffer como o OP estava pedindo. Mas ao manipular um ArrayBuffer, quase sempre envolvo-o com Uint8Array ou algo semelhante, então deve estar próximo do que está sendo solicitado.

DoomGoober
fonte
2

JS puro - sem meio-passo de string (sem atob)

Escrevo a seguinte função que converte base64 de maneira direta (sem conversão para string no meio do caminho). IDÉIA

  • obter um bloco de 4 caracteres de base64
  • encontre o índice de cada caractere no alfabeto base64
  • converter índice em número de 6 bits (string binária)
  • junte quatro números de 6 bits que dá um número de 24 bits (armazenado como string binária)
  • dividir string de 24 bits em três de 8 bits e converter cada uma para numerar e armazená-las na matriz de saída
  • caso de canto: se a string de base64 de entrada terminar com um / dois =caracteres, remova um / dois números da matriz de saída

A solução abaixo permite processar grandes strings de entrada de base64. Função semelhante para converter bytes em base64 sem btoa está AQUI

Kamil Kiełczewski
fonte
então não falta "."?
Gillsoft AB
Teste em um navegador, não tenho certeza se este é o resultado esperado? "Alice's Adventure in Wonderland " (ou seja, o último personagem é NaN)
Gillsoft AB
1
@GillsoftAB, obrigado por esta informação - você está certo - Eu
resolvo
-3
const str = "dGhpcyBpcyBiYXNlNjQgc3RyaW5n"
const encoded = new TextEncoder().encode(str) // is Uint8Array
const buf = encoded.buffer // is ArrayBuffer
Andrii Nemchenko
fonte
6
Observe que isso não executa nenhuma decodificação / codificação Base64. Ele apenas transforma os 6 bytes de "base64" em um ArrayBuffer ou Uint8Array de 6 elementos.
dubek de
2
@dubek foi isso que foi perguntado.
Andrii Nemchenko de