ArrayBuffer para string codificada em base64

203

Preciso de uma maneira eficiente (leia-se nativa) para converter uma ArrayBufferstring em base64, que precisa ser usada em uma postagem com várias partes.

zaheer
fonte

Respostas:

222
function _arrayBufferToBase64( buffer ) {
    var binary = '';
    var bytes = new Uint8Array( buffer );
    var len = bytes.byteLength;
    for (var i = 0; i < len; i++) {
        binary += String.fromCharCode( bytes[ i ] );
    }
    return window.btoa( binary );
}

mas implementações não nativas são mais rápidas, por exemplo, https://gist.github.com/958841, consulte http://jsperf.com/encoding-xhr-image-data/6

mobz
fonte
10
Tentei a implementação não nativa a partir do link e demorou 1 minuto e meio para converter um buffer de tamanho de 1 M, enquanto o código do loop acima levou apenas 1 segundo.
cshu
1
Eu gosto da simplicidade dessa abordagem, mas toda essa concatenação de strings pode ser cara. Parece que a construção de uma série de personagens e join()ing-los no final é significativamente mais rápido no Firefox, IE e Safari (mas bastante mais lento no Chrome): jsperf.com/tobase64-implementations
JLRishe
Estou usando esta função para conversão de buffer de matriz em base64, mas não consigo recuperar o buffer de matriz. Tenho wrriten uma função _base64ToArrayBuffer () aqui: codeshare.io/PT4pb mas que me dá um erro como:Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.
bawejakunal
isso não é trabalhado neste JSZip. i encontrar uma outra maneira github.com/michael/github/issues/137
RouR
1
Estou tentando fazer o upload de arquivos PDF de 50mb usando angualrjs e webapi2. Estou usando o código de linha acima. Após o upload do arquivo, a página foi travada e travada. Abaixo da linha de código, eu estava sendo usado, mas obtendo valor nulo no método webapi. "var base64String = btoa (String.fromCharCode.apply (nulo, novo Uint8Array (arrayBuffer)));" por favor sugerir alguma idéia ...
Prabhakaran S
102

Este trabalho é bom para mim:

var base64String = btoa(String.fromCharCode.apply(null, new Uint8Array(arrayBuffer)));

No ES6, a sintaxe é um pouco mais simples:

let base64String = btoa(String.fromCharCode(...new Uint8Array(arrayBuffer)));

Conforme indicado nos comentários, esse método pode resultar em um erro de tempo de execução em alguns navegadores quando o ArrayBuffertamanho é grande. O limite exato de tamanho depende da implementação em qualquer caso.

GOTO 0
fonte
38
Eu gosto mais desse método por concisão, mas recebo um "erro máximo no tamanho máximo da pilha de chamadas". A técnica de loop acima contorna isso.
Jay,
13
Também estou recebendo um erro de tamanho de pilha, então usei a resposta do mobz e funcionou muito bem.
David Jones
12
Não funcionou para buffers grandes. Ligeira modificação para fazê-lo funcionar::btoa([].reduce.call(new Uint8Array(bufferArray),function(p,c){return p+String.fromCharCode(c)},''))
laggingreflex
1
Estou tentando fazer o upload de arquivos PDF de 50mb usando angualrjs e webapi2. Estou usando o código de linha acima. Após o upload do arquivo, a página foi travada e travada. Abaixo da linha de código, eu estava sendo usado, mas obtendo valor nulo no método webapi. "var base64String = btoa (String.fromCharCode.apply (nulo, novo Uint8Array (arrayBuffer)));" por favor sugerir alguma idéia ...
Prabhakaran S
2
O @Kugel btoaé seguro para caracteres no intervalo de código de 0 a 255, pois esse é o caso (pense nos 8 pol Uint8Array.).
GOTO 0
42

Para quem gosta de curtir, aqui está outro Array.reduceque não causará estouro de pilha:

var base64 = btoa(
  new Uint8Array(arrayBuffer)
    .reduce((data, byte) => data + String.fromCharCode(byte), '')
);
gkunz
fonte
4
Não tenho certeza se isso é realmente sexy. Afinal, você está criando <amount of Bytes in the buffer>novas strings.
Neonit 28/03
Que tal btoa(new Uint8Array(arraybuffer).reduce((data,byte)=>(data.push(String.fromCharCode(byte)),data),[]).join(''))?
Roy Tinker
35

Há outra maneira assíncrona de usar Blob e FileReader.

Eu não testei o desempenho. Mas é uma maneira diferente de pensar.

function arrayBufferToBase64( buffer, callback ) {
    var blob = new Blob([buffer],{type:'application/octet-binary'});
    var reader = new FileReader();
    reader.onload = function(evt){
        var dataurl = evt.target.result;
        callback(dataurl.substr(dataurl.indexOf(',')+1));
    };
    reader.readAsDataURL(blob);
}

//example:
var buf = new Uint8Array([11,22,33]);
arrayBufferToBase64(buf, console.log.bind(console)); //"CxYh"
cuixiping
fonte
Use em dataurl.split(',', 2)[1]vez de dataurl.substr(dataurl.indexOf(',')+1).
Carter Medlin
Isso não parece estar garantido para o trabalho. De acordo com w3c.github.io/FileAPI/#issue-f80bda5b readAsDataURL teoricamente poderia retornar um por cento codificado dataURI (E parece que é realmente o caso em jsdom )
TS
@CarterMedlin Por que splitseria melhor do que substring?
TS
a divisão é mais curta. mas dataurl pode conter uma ou mais vírgulas (,), a divisão não é segura.
cuixiping
12

Eu usei isso e funciona para mim.

function arrayBufferToBase64( buffer ) {
    var binary = '';
    var bytes = new Uint8Array( buffer );
    var len = bytes.byteLength;
    for (var i = 0; i < len; i++) {
        binary += String.fromCharCode( bytes[ i ] );
    }
    return window.btoa( binary );
}



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;
}
Elias Vargas
fonte
Não é seguro. Veja @chemoish resposta
Kugel
11

Minha recomendação para isso é NÃO usar btoaestratégias nativas , pois elas não codificam corretamente todasArrayBuffer as…

reescreva os DOMs atob () e btoa ()

Como DOMStrings são cadeias de caracteres de 16 bits, na maioria dos navegadores que chamam window.btoa em uma cadeia de caracteres Unicode causará uma exceção de caracteres fora do intervalo se um caractere exceder o intervalo de um caractere de 8 bits codificado em ASCII.

Embora nunca tenha encontrado esse erro exato, descobri que muitos dos ArrayBuffer que tentei codificar foram codificados incorretamente.

Eu usaria recomendação MDN ou essência.

chemoish
fonte
btoanão funciona em String, mas o OP está perguntando ArrayBuffer.
TSH
1
Muito isso, tantos trechos aqui que recomendam a coisa errada! Eu já vi esse erro várias vezes, onde as pessoas usam cegamente atob e btoa.
precisa saber é o seguinte
4
Todos os buffers de matriz devem ser bem codificados usando as estratégias em outras respostas, atob / btoa é apenas um problema para texto que contém caracteres maiores que 0xFF (que matrizes de bytes por definição não). O aviso MDN não se aplica porque, ao usar a estratégia nas outras respostas, é garantido que você tenha uma sequência que consiste apenas em caracteres ASCII, já que qualquer valor de um Uint8Array é garantido entre 0 e 255, o que significa que String.fromCharCode é garantido para retornar um personagem que não está fora do alcance.
Jamesernator
11
var blob = new Blob([arrayBuffer])

var reader = new FileReader();
reader.onload = function(event){
   var base64 =   event.target.result
};

reader.readAsDataURL(blob);
Alex
fonte
1
Adicione alguma explicação à sua resposta, por favor. O que este código significa?
Oscar
Exemplo MDN útil semelhante .
Spenceryue
1
este é, de longe, a abordagem mais rápida - dezenas de vezes mais rápido do que os outros em meus testes limitados
Jitin
Eu gostaria de ter encontrado esta solução como 8 horas novamente. meu dia não teria sido desperdiçado; (obrigado
Kaiser Shahid
7

Abaixo estão 2 funções simples para converter Uint8Array em Base64 String e vice-versa

arrayToBase64String(a) {
    return btoa(String.fromCharCode(...a));
}

base64StringToArray(s) {
    let asciiString = atob(s);
    return new Uint8Array([...asciiString].map(char => char.charCodeAt(0)));
}
Steve Dixon
fonte
1
Esta é uma resposta confusa. Isso não parece JavaScript válido e é um Uint8Array um ArrayBuffer?
user1153660
@ user1153660 Adicione a functionpalavra-chave e ela deverá funcionar em um navegador moderno.
Yeti
Impressionante! btoa (String.fromCharCode (... a)); é a versão mais curta que vi até agora para codificar Uint8Array.
Nicolo
1
Parece bom, mas se a matriz for muito grande, lançará o erro máximo do tamanho máximo da pilha de chamadas.
Paweł Psztyć
0

Você pode derivar uma matriz normal do ArrayBufferusando Array.prototype.slice. Use uma função como Array.prototype.mapconverter bytes em caracteres e joineles juntos para formar uma string.

function arrayBufferToBase64(ab){

    var dView = new Uint8Array(ab);   //Get a byte view        

    var arr = Array.prototype.slice.call(dView); //Create a normal array        

    var arr1 = arr.map(function(item){        
      return String.fromCharCode(item);    //Convert
    });

    return window.btoa(arr1.join(''));   //Form a string

}

Esse método é mais rápido, pois não há concatenações de strings em execução.

Charlie
fonte
Não é seguro. Veja @chemoish resposta
Kugel
0

O OP não especificou o ambiente em execução, mas se você estiver usando o Node.JS, há uma maneira muito simples de fazer isso.

De acordo com os documentos oficiais do Node.JS https://nodejs.org/api/buffer.html#buffer_buffers_and_character_encodings

// This step is only necessary if you don't already have a Buffer Object
const buffer = Buffer.from(yourArrayBuffer);

const base64String = buffer.toString('base64');

Além disso, se você estiver executando em Angular, por exemplo, a Classe Buffer também será disponibilizada em um Ambiente de Navegador.

João Eduardo Soares e Silva
fonte
Sua resposta se aplica apenas ao NodeJS e não funcionará no navegador.
jvatic 9/03
1
@ jvatic eu vejo, o OP não especificou claramente o ambiente de execução, então minha resposta não está incorreta, ele apenas marcou Javascript. Atualizei minha resposta para torná-la mais concisa. Acho que essa é uma resposta importante, porque estava pesquisando como fazer isso e não consegui encontrar a melhor resposta para o problema.
João Eduardo Soares e Silva
-3

Ao meu lado, usando o navegador Chrome, tive que usar o DataView () para ler um arrayBuffer

function _arrayBufferToBase64( tabU8A ) {
var binary = '';
let lecteur_de_donnees = new DataView(tabU8A);
var len = lecteur_de_donnees.byteLength;
var chaine = '';
var pos1;
for (var i = 0; i < len; i++) {
    binary += String.fromCharCode( lecteur_de_donnees.getUint8( i ) );
}
chaine = window.btoa( binary )
return chaine;}
Louvet Jean
fonte
-4
function _arrayBufferToBase64(uarr) {
    var strings = [], chunksize = 0xffff;
    var len = uarr.length;

    for (var i = 0; i * chunksize < len; i++){
        strings.push(String.fromCharCode.apply(null, uarr.subarray(i * chunksize, (i + 1) * chunksize)));
    }

    return strings.join("");
}

Isso é melhor, se você usar JSZip para descompactar archive da string

RouR
fonte