Node.js genera l'errore "btoa non definito"


231

Nella mia applicazione node.js ho fatto un npm install btoa-atobmodo per poter usare le funzioni btoa () e atob () che sono native nel javascript sul lato client ma per qualche ragione non erano incluse nel nodo. La nuova directory è stata mostrata nella mia cartella node_modules, che è essa stessa root insieme a app.js. Quindi mi sono assicurato di aggiungere btoa-atob come dipendenza nel mio file package.json che è root.

Tuttavia, per qualche motivo, non funzionerà ancora.

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

^ dovrebbe generare "SGVsbG8gV29ybGQh" sulla console, ma viene visualizzato l'errore "btoa non definito".

Non ho eseguito correttamente l'installazione? Cosa ho trascurato?

Risposte:


539

Il modulo 'btoa-atob' non esporta un'interfaccia programmatica, ma fornisce solo utilità da riga di comando.

Se è necessario convertire in Base64, è possibile farlo utilizzando Buffer:

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

Inverti (supponendo che il contenuto che stai decodificando sia una stringa utf8):

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

Nota: prima di Nodo v4, utilizzare new Bufferanziché Buffer.from.


57

Le soluzioni pubblicate qui non funzionano con caratteri non ascii (ovvero se prevedi di scambiare base64 tra Node.js e un browser). Per farlo funzionare devi contrassegnare il testo di input come 'binario'.

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

Questo ti dà SOlsbPMgd/NybGQhIQ==. Se esegui atob('SOlsbPMgd/NybGQhIQ==')un browser, lo decodificherà nel modo giusto. Lo farà anche in Node.js tramite:

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

Se non esegui la "parte binaria", decodificherai erroneamente i caratteri speciali.

L'ho preso dall'implementazione del pacchetto btoa npm :


1
grazie, stavo impazzendo con i personaggi cambiati.
Matthew James Briggs,

1
Grazie Ivan, avrei trascorso ore in questo ... la tua risposta dovrebbe essere quella accettata!
Pawel,

Iván Alegre Basta non usare la codifica "binaria". Se lo fai Buffer.from('Hélló wórld!!').toString('base64'), ti darà SOlsbPMgd/NybGQhIQ==quale può essere convertito correttamente in stringa non ascii.
TotalAMD

1
@TotalAMD non funzionerà scambiando base64 da Node.js al browser o viceversa
Iván Alegre,

3
Stai confrontando la codifica in base64 e decodificandola nella stessa piattaforma. Da Chrome a Chrome e da Nodo a Nodo. Se lo codifichi nel Nodo 10 senza binario, lo darà SMOpbGzDsyB3w7NybGQhIQ==. Se decodifichi questo in un browser, questo ti darà Hélló wórld!!. Il binario è perfetto per garantire la compatibilità multipiattaforma.
Iván Alegre,

22

Il mio team ha riscontrato questo problema durante l'utilizzo di Node con React Native e PouchDB. Ecco come l'abbiamo risolto ...

Buffer di installazione NPM:

$ npm install --save buffer

Assicurarsi Buffer, btoae atobsono caricati come globali:

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
Il comando new Buffer () nel codice genera il seguente errore nelle versioni più recenti del nodo: [DEP0005] DeprecationWarning: Buffer () è obsoleto a causa di problemi di sicurezza e usabilità. Utilizzare invece i metodi Buffer.alloc (), Buffer.allocUnsafe () o Buffer.from ().
Rodrigo De Almeida Siqueira,

8

Ho scoperto che sebbene gli spessori delle risposte sopra funzionassero, non corrispondevano al comportamento delle implementazioni dei browser desktop di 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])));

A quanto pare, le Bufferistanze rappresentano / interpretano le stringhe codificate in UTF-8 per impostazione predefinita . Al contrario, in Chrome desktop, non puoi nemmeno inserire una stringa che contiene caratteri al di fuori dell'intervallo latino1 btoa(), poiché genererà un'eccezione:Uncaught DOMException: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.

Pertanto, è necessario impostare in modo esplicito il tipo di codifica per latin1in ordine per i Node.js spessorare in base al tipo di codifica di Chrome desktop:

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

nel nodo v0.12.2 non esiste una funzione
Buffer.from

@Zibri Node v0.12.2 è antico e ha raggiunto la fine della sua vita due anni fa . Buffer.from()è il modo consigliato di utilizzare l'API buffer per motivi di sicurezza (anche se quel collegamento chiarirà le alternative a quelle Buffer.from()che potrebbero essere applicate per il nodo v0.12.2).
Jamie Birch,

Lo capisco, ma su un dispositivo incorporato ho quella versione.
Zibri,

Sto eseguendo il mio codice in Atom usando il pacchetto di script github.com/rgbkrk/atom-script che è una vecchia implementazione di nodo. In altre parole, ha anche bisogno di un'implementazione per btoa, mentre non può far fronte a Buffer.from ().
Shrimpy,

4

Ho un codice condiviso tra server e client e avevo bisogno di un'implementazione di btoa al suo interno. Ho provato a fare qualcosa del tipo:

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

ma il server si distruggerebbe con:

Errore di riferimento: btoa non è definito

mentre Buffernon è definito sul client.

Non ho potuto controllare window.btoa (è un codice condiviso, ricordi?)

Quindi ho finito con questa implementazione:

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

1

Capisco che questo è un punto di discussione per un'applicazione nodo, ma nell'interesse delle applicazioni JavaScript universali in esecuzione su un server nodo, ecco come sono arrivato a questo post, ho cercato questo per un'app di reazione universale / isomorfa e il pacchetto ha ababfunzionato per me. In realtà era l'unica soluzione che riuscivo a trovare che funzionasse, piuttosto che usare anche il metodo Buffer menzionato (avevo problemi di dattiloscritto).

(Questo pacchetto viene utilizzato da jsdom, che a sua volta viene utilizzato dal windowpacchetto.)

Tornando al mio punto; in base a questo, forse se questa funzionalità è già scritta come un pacchetto npm come quello che hai menzionato e ha il suo algoritmo basato sulle specifiche W3, potresti installare e utilizzare il ababpacchetto piuttosto che scrivere la tua funzione che potrebbe essere o meno accurato in base alla codifica.

--- MODIFICA ---

Ho iniziato ad avere strani problemi oggi con la codifica (non so perché abbia iniziato a succedere ora) con il pacchetto abab. Sembra codificare correttamente la maggior parte delle volte, ma a volte sul front-end codifica in modo errato. Ho passato molto tempo a provare a eseguire il debug, ma è passato al pacchetto base-64come raccomandato e ha funzionato immediatamente. Sicuramente sembrava essere dovuto all'algoritmo base64 di abab.


1

Stesso problema con il plug-in 'script' nell'editor Atom, che è una vecchia versione del nodo, che non ha btoa (), né atob (), né supporta il tipo di dati Buffer. Il seguente codice fa il trucco:

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


Questo funziona grazie. Nel mio caso, sto usando ChakraEngine che non sembra supportare atob.
Acqua il


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

Ecco una concisa soluzione universale per la codifica base64:

const nodeBtoa = (b) => Buffer.from(b).toString('base64');
export const base64encode = typeof btoa !== 'undefined' ? btoa : nodeBtoa;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.