Node.js arroja el error "btoa no está definido"

231

En mi aplicación node.js hice una npm install btoa-atobpara poder usar las funciones btoa () y atob () que son nativas en javascript del lado del cliente pero por alguna razón no se incluyeron en el nodo. El nuevo directorio apareció en mi carpeta node_modules, que está en la raíz junto con app.js. Luego me aseguré de agregar btoa-atob como una dependencia en mi archivo package.json que está en la raíz.

Sin embargo, por alguna razón, todavía no funcionará.

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

^ debería mostrar "SGVsbG8gV29ybGQh" en la consola, pero en su lugar, aparece el error "btoa no está definido".

¿No hice la instalación correctamente? ¿Qué pasé por alto?

Joey
fuente

Respuestas:

539

El módulo 'btoa-atob' no exporta una interfaz programática, solo proporciona utilidades de línea de comandos.

Si necesita convertir a Base64, puede hacerlo usando Buffer:

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

Reverso (suponiendo que el contenido que está decodificando es una cadena utf8):

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

Nota: antes del Nodo v4, use en new Bufferlugar de Buffer.from.

mscdex
fuente
57

Las soluciones publicadas aquí no funcionan en caracteres no ASCII (es decir, si planea intercambiar base64 entre Node.js y un navegador). Para que funcione, debe marcar el texto de entrada como 'binario'.

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

Esto te da SOlsbPMgd/NybGQhIQ==. Si realiza atob('SOlsbPMgd/NybGQhIQ==')en un navegador, lo decodificará de la manera correcta. Lo hará bien también en Node.js a través de:

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

Si no hace la "parte binaria", decodificará incorrectamente los caracteres especiales.

Lo obtuve de la implementación del paquete btoa npm :

Iván Alegre
fuente
1
gracias, me estaba volviendo loco con los personajes cambiados.
Matthew James Briggs el
1
Gracias Ivan, hubiera pasado horas en esto ... ¡tu respuesta debería ser la aceptada!
Pawel
Iván Alegre Simplemente no use la codificación 'binaria'. Si lo hace Buffer.from('Hélló wórld!!').toString('base64'), le dará SOlsbPMgd/NybGQhIQ==qué se puede convertir a una cadena no ASCII correctamente.
TotalAMD
1
@TotalAMD no funcionará intercambiando base64 de Node.js al navegador o viceversa
Iván Alegre
3
Está comparando la codificación en base64 y decodificándola en la misma plataforma. Chrome to Chrome y Node to Node. Si lo codifica en el Nodo 10 sin binario, dará SMOpbGzDsyB3w7NybGQhIQ==. Si decodifica esto en un navegador, le dará Hélló wórld!!. El binario es perfecto para garantizar la compatibilidad multiplataforma.
Iván Alegre
22

Mi equipo se encontró con este problema al usar Node con React Native y PouchDB. Así es como lo resolvimos ...

NPM buffer de instalación:

$ npm install --save buffer

Asegúrese Buffer, btoay atobse cargan como globales:

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');
  };
}
PaulMest
fuente
2
El comando new Buffer () en su código da el siguiente error en las versiones más nuevas del nodo: [DEP0005] DeprecationWarning: Buffer () está en desuso debido a problemas de seguridad y usabilidad. Utilice los métodos Buffer.alloc (), Buffer.allocUnsafe () o Buffer.from () en su lugar.
Rodrigo De Almeida Siqueira
8

Descubrí que, aunque las cuñas de las respuestas anteriores funcionaron, no coincidían con el comportamiento de las implementaciones de los navegadores de escritorio de btoa()y 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 resultado, las Bufferinstancias representan / interpretan cadenas codificadas en UTF-8 de forma predeterminada . Por el contrario, en Chrome de escritorio, ni siquiera puede ingresar una cadena que contenga caracteres fuera del rango latin1 btoa(), ya que arrojará una excepción:Uncaught DOMException: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.

Por lo tanto, es necesario establecer de forma explícita el tipo de codificación para latin1el fin para sus Node.js calce para que coincida con el tipo de codificación de escritorio 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])));
Jamie Birch
fuente
en el nodo v0.12.2 no hay una función
Buffer.from
@Zibri Node v0.12.2 es antiguo y llegó al final de su vida hace dos años . Buffer.from()es la forma recomendada de usar la API de búfer por razones de seguridad (aunque ese enlace aclarará alternativas a las Buffer.from()que pueden aplicarse para el Nodo v0.12.2).
Jamie Birch
Entiendo eso, pero en un dispositivo incorporado tengo esa versión.
Zibri
Estoy ejecutando mi código en Atom usando el paquete de script github.com/rgbkrk/atom-script que es una implementación antigua del nodo. En otras palabras, también necesita una implementación para btoa, mientras que no puede hacer frente a Buffer.from ().
Shrimpy
4

Tengo un código compartido entre el servidor y el cliente y necesitaba una implementación de btoa dentro de él. Intenté hacer algo como:

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

pero el servidor se enamoraría de:

ReferenceError: btoa no está definido

while Bufferno está definido en el cliente.

No pude comprobar window.btoa (es un código compartido, ¿recuerdas?)

Así que terminé con esta implementación:

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

Entiendo que este es un punto de discusión para una aplicación de nodo, pero en interés de las aplicaciones universales de JavaScript que se ejecutan en un servidor de nodo, que es cómo llegué a esta publicación, he estado investigando esto para una aplicación de reacción universal / isomórfica. edificio, y el paquete ababfuncionó para mí. De hecho, fue la única solución que pude encontrar que funcionó, en lugar de usar el método Buffer también mencionado (tuve problemas con el mecanografiado).

(Este paquete es usado por jsdom, que a su vez es usado por el windowpaquete)

Volviendo a mi punto; basado en esto, quizás si esta funcionalidad ya está escrita como un paquete npm como el que mencionó, y tiene su propio algoritmo basado en la especificación W3, podría instalar y usar el ababpaquete en lugar de escribir su propia función que puede o no ser precisa basada en la codificación.

--- EDITAR ---

Comencé a tener problemas extraños hoy con la codificación (no estoy seguro de por qué comenzó a suceder ahora) con el paquete abab. Parece que codifica correctamente la mayor parte del tiempo, pero a veces en el front end codifica incorrectamente. Pasé mucho tiempo tratando de depurar, pero cambié al paquete base-64según lo recomendado, y funcionó de inmediato. Definitivamente parecía estar en el algoritmo base64 de abab.

Phil Gibbins
fuente
1

El mismo problema con el complemento 'script' en el editor Atom, que es una versión antigua del nodo, que no tiene btoa (), ni atob (), ni es compatible con el tipo de datos Buffer. El siguiente código hace el truco:

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

Camarones
fuente
Esto funciona gracias. En mi caso, estoy usando ChakraEngine que no parece admitir atob.
Agua
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();
  }
};
Ali eslamifard
fuente
0

Aquí hay una solución universal concisa para la codificación base64:

const nodeBtoa = (b) => Buffer.from(b).toString('base64');
export const base64encode = typeof btoa !== 'undefined' ? btoa : nodeBtoa;
Luke Taylor
fuente