O Node.js lança o erro "btoa não está definido"


230

No meu aplicativo node.js, fiz um procedimento npm install btoa-atobpara poder usar as funções btoa () e atob () que são nativas no javascript do lado do cliente, mas por algum motivo não foram incluídas no nó. O novo diretório apareceu na minha pasta node_modules, que está na raiz ao lado de app.js. Então, certifique-se de adicionar o btoa-atob como uma dependência no meu arquivo package.json, que está na raiz.

No entanto, por algum motivo, ainda não funcionará.

console.log(btoa("Hello World!"));

^ deve gerar "SGVsbG8gV29ybGQh" no console, mas, em vez disso, recebo o erro "btoa não está definido".

Não fiz a instalação corretamente? O que eu esqueci?

Respostas:


538

O módulo 'btoa-atob' não exporta uma interface programática, apenas fornece utilitários de linha de comando.

Se você precisar converter para Base64, poderá fazê-lo usando o Buffer:

console.log(Buffer.from('Hello World!').toString('base64'));

Reverso (assumindo que o conteúdo que você está decodificando é uma string utf8):

console.log(Buffer.from(b64Encoded, 'base64').toString());

Nota: antes do Nó v4, use em new Buffervez de Buffer.from.


57

As soluções postadas aqui não funcionam com caracteres não-ascii (por exemplo, se você planeja trocar o base64 entre o Node.js e um navegador). Para fazê-lo funcionar, você deve marcar o texto de entrada como 'binário'.

Buffer.from('Hélló wórld!!', 'binary').toString('base64')

Isso te dá SOlsbPMgd/NybGQhIQ==. Se você criar atob('SOlsbPMgd/NybGQhIQ==')um navegador, ele será decodificado da maneira correta. Isso também funcionará no Node.js através de:

Buffer.from('SOlsbPMgd/NybGQhIQ==', 'base64').toString('binary')

Se você não fizer a "parte binária", decodificará incorretamente os caracteres especiais.

Eu entendi da implementação do pacote btoa npm :


1
obrigado, eu estava ficando louco com personagens mudados.
Matthew James Briggs

1
Obrigado Ivan, eu teria passado horas nisso ... sua resposta deve ser a aceita!
Pawel

Iván Alegre Apenas não use a codificação 'binária'. Se você o fizer, Buffer.from('Hélló wórld!!').toString('base64')ele fornecerá o SOlsbPMgd/NybGQhIQ==que pode ser convertido em sequência não-ascii corretamente.
TotalAMD

1
@TotalAMD não vai funcionar troca de base64 de Node.js para o browser ou vice-versa
Iván Alegre

3
Você está comparando a codificação em base64 e decodificando-a na mesma plataforma. Chrome para Chrome e Nó para Nó. Se você o codificar no Nó 10 sem binário, ele dará SMOpbGzDsyB3w7NybGQhIQ==. Se você decodificar isso em um navegador, ele fornecerá Hélló wórld!!. O binário é perfeito para garantir a compatibilidade entre plataformas.
Iván Alegre

22

Minha equipe teve esse problema ao usar o Node com React Native e PouchDB. Aqui está como resolvemos isso ...

Buffer de instalação do NPM:

$ npm install --save buffer

Certifique-se de Buffer, btoae atobsão carregados como uma globais:

global.Buffer = global.Buffer || require('buffer').Buffer;

if (typeof btoa === 'undefined') {
  global.btoa = function (str) {
    return new Buffer(str, 'binary').toString('base64');
  };
}

if (typeof atob === 'undefined') {
  global.atob = function (b64Encoded) {
    return new Buffer(b64Encoded, 'base64').toString('binary');
  };
}

2
O comando new Buffer () no seu código fornece o seguinte erro nas versões mais recentes do nó: [DEP0005] DeprecationWarning: Buffer () foi descontinuado devido a problemas de segurança e usabilidade. Por favor, use os métodos Buffer.alloc (), Buffer.allocUnsafe () ou Buffer.from ().
Rodrigo De Almeida Siqueira

8

Descobri que, embora os shims das respostas acima funcionassem, eles não correspondiam ao comportamento das implementações dos navegadores de desktop btoa()e atob():

const btoa = function(str){ return Buffer.from(str).toString('base64'); }
// returns "4pyT", yet in desktop Chrome would throw an error.
btoa('✓');
// returns "fsO1w6bCvA==", yet in desktop Chrome would return "fvXmvA=="
btoa(String.fromCharCode.apply(null, new Uint8Array([0x7e, 0xf5, 0xe6, 0xbc])));

Como se vê, as Bufferinstâncias representam / interpretam seqüências de caracteres codificadas em UTF-8 por padrão . Por outro lado, no Chrome da área de trabalho, você não pode nem inserir uma sequência que contenha caracteres fora do intervalo latin1 btoa(), pois isso gera uma exceção:Uncaught DOMException: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.

Portanto, é preciso definir explicitamente o tipo de codificação para latin1para que seus Node.js calço para coincidir com o tipo de codificação de área de trabalho Chrome:

const btoaLatin1 = function(str) { return Buffer.from(str, 'latin1').toString('base64'); }
const atobLatin1 = function(b64Encoded) {return Buffer.from(b64Encoded, 'base64').toString('latin1');}

const btoaUTF8 = function(str) { return Buffer.from(str, 'utf8').toString('base64'); }
const atobUTF8 = function(b64Encoded) {return Buffer.from(b64Encoded, 'base64').toString('utf8');}

btoaLatin1('✓'); // returns "Ew==" (would be preferable for it to throw error because this is undecodable)
atobLatin1(btoa('✓')); // returns "\u0019" (END OF MEDIUM)

btoaUTF8('✓'); // returns "4pyT"
atobUTF8(btoa('✓')); // returns "✓"

// returns "fvXmvA==", just like desktop Chrome
btoaLatin1(String.fromCharCode.apply(null, new Uint8Array([0x7e, 0xf5, 0xe6, 0xbc])));
// returns "fsO1w6bCvA=="
btoaUTF8(String.fromCharCode.apply(null, new Uint8Array([0x7e, 0xf5, 0xe6, 0xbc])));

no nó v0.12.2 não é uma função Buffer.from
Zibri

O @Zibri Node v0.12.2 é antigo e chegou ao fim da vida útil há dois anos . Buffer.from()é a maneira recomendada de usar a API do buffer devido a razões de segurança (embora esse link esclareça alternativas ao Buffer.from()que pode ser aplicável ao nó v0.12.2).
Jamie Birch

Eu entendo isso, mas em um dispositivo incorporado eu tenho essa versão.
Zibri

Estou executando meu código no Atom usando o pacote de scripts github.com/rgbkrk/atom-script que é uma implementação antiga do nó. Em outras palavras, ele também precisa de uma implementação para btoa, embora não possa lidar com Buffer.from ().
Shrimpy

4

Eu tenho um código compartilhado entre servidor e cliente e eu precisava de uma implementação de btoa dentro dele. Eu tentei fazer algo como:

const btoaImplementation =  btoa || (str => Buffer.from(str).toString('base64'));

mas o servidor esmagaria com:

ReferenceError: btoa não está definido

while Buffernão está definido no cliente.

Não pude verificar window.btoa (é um código compartilhado, lembra?)

Então eu terminei com esta implementação:

const btoaImplementation = str => {
    try {
        return btoa(str);
    } catch(err) {
        return Buffer.from(str).toString('base64')
    }
};

1

Entendo que este é um ponto de discussão para um aplicativo de nó, mas no interesse de aplicativos JavaScript universais em execução em um servidor de nó, e foi assim que cheguei neste post, pesquisei isso para um aplicativo de reação universal / isomórfico em que estive. edifício, e o pacote ababfuncionou para mim. De fato, foi a única solução que pude encontrar que funcionou, em vez de usar o método Buffer também mencionado (eu tinha problemas de digitação).

(Este pacote é usado por jsdom, que por sua vez é usado pelowindow pacote.)

Voltando ao meu ponto; com base nisso, talvez se essa funcionalidade já estiver escrita como um pacote npm como o que você mencionou e tiver seu próprio algoritmo baseado na especificação W3, você poderá instalar e usar oabab pacote em vez de escrever sua própria função que pode ou não ser preciso com base na codificação.

--- EDIT ---

Hoje comecei a ter problemas estranhos hoje com a codificação (não sei por que isso começou a acontecer agora) com o pacote abab. Parece codificar corretamente na maioria das vezes, mas às vezes no front-end codifica incorretamente. Passei muito tempo tentando depurar, mas mudei para o pacote base-64conforme recomendado e funcionou imediatamente. Definitivamente parecia estar no algoritmo base64 de abab.


1

O mesmo problema com o plug-in 'script' no editor Atom, que é uma versão antiga do nó, sem btoa () nem atob (), nem suporta o tipo de dados Buffer. O código a seguir faz o truque:

var Base64 = new function() {
  var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
  this.encode = function(input) {
    var output = "";
    var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
    var i = 0;
    input = Base64._utf8_encode(input);
    while (i < input.length) {
      chr1 = input.charCodeAt(i++);
      chr2 = input.charCodeAt(i++);
      chr3 = input.charCodeAt(i++);
      enc1 = chr1 >> 2;
      enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
      enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
      enc4 = chr3 & 63;
      if (isNaN(chr2)) {
        enc3 = enc4 = 64;
      } else if (isNaN(chr3)) {
        enc4 = 64;
      }
      output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4);
    }
    return output;
  }

  this.decode = function(input) {
    var output = "";
    var chr1, chr2, chr3;
    var enc1, enc2, enc3, enc4;
    var i = 0;
    input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
    while (i < input.length) {
      enc1 = keyStr.indexOf(input.charAt(i++));
      enc2 = keyStr.indexOf(input.charAt(i++));
      enc3 = keyStr.indexOf(input.charAt(i++));
      enc4 = keyStr.indexOf(input.charAt(i++));
      chr1 = (enc1 << 2) | (enc2 >> 4);
      chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
      chr3 = ((enc3 & 3) << 6) | enc4;
      output = output + String.fromCharCode(chr1);
      if (enc3 != 64) {
        output = output + String.fromCharCode(chr2);
      }
      if (enc4 != 64) {
        output = output + String.fromCharCode(chr3);
      }
    }
    output = Base64._utf8_decode(output);
    return output;
  }

  this._utf8_encode = function(string) {
    string = string.replace(/\r\n/g, "\n");
    var utftext = "";
    for (var n = 0; n < string.length; n++) {
      var c = string.charCodeAt(n);
      if (c < 128) {
        utftext += String.fromCharCode(c);
      } else if ((c > 127) && (c < 2048)) {
        utftext += String.fromCharCode((c >> 6) | 192);
        utftext += String.fromCharCode((c & 63) | 128);
      } else {
        utftext += String.fromCharCode((c >> 12) | 224);
        utftext += String.fromCharCode(((c >> 6) & 63) | 128);
        utftext += String.fromCharCode((c & 63) | 128);
      }
    }
    return utftext;
  }

  this._utf8_decode = function(utftext) {
    var string = "";
    var i = 0;
    var c = 0,
      c1 = 0,
      c2 = 0,
      c3 = 0;
    while (i < utftext.length) {
      c = utftext.charCodeAt(i);
      if (c < 128) {
        string += String.fromCharCode(c);
        i++;
      } else if ((c > 191) && (c < 224)) {
        c2 = utftext.charCodeAt(i + 1);
        string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
        i += 2;
      } else {
        c2 = utftext.charCodeAt(i + 1);
        c3 = utftext.charCodeAt(i + 2);
        string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
        i += 3;
      }
    }
    return string;
  }
}()

var btoa = Base64.encode;
var atob = Base64.decode;

console.log("btoa('A') = " + btoa('A'));
console.log("btoa('QQ==') = " + atob('QQ=='));
console.log("btoa('B') = " + btoa('B'));
console.log("btoa('Qg==') = " + atob('Qg=='));


Isso funciona, obrigado. No meu caso, estou usando o ChakraEngine, que parece não suportar atob.
Water


0
export const universalBtoa = str => {
  try {
    return btoa(str);
  } catch (err) {
    return Buffer.from(str).toString('base64');
  }
};

export const universalAtob = b64Encoded => {
  try {
    return atob(b64Encoded);
  } catch (err) {
    return Buffer.from(b64Encoded, 'base64').toString();
  }
};

0

Aqui está uma solução universal concisa para a codificação base64:

const nodeBtoa = (b) => Buffer.from(b).toString('base64');
export const base64encode = typeof btoa !== 'undefined' ? btoa : nodeBtoa;
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.