Node.js zgłasza błąd „btoa nie jest zdefiniowany”


230

W mojej aplikacji node.js zrobiłem to, npm install btoa-atobaby móc korzystać z funkcji btoa () i atob (), które są rodzime w javascript po stronie klienta, ale z jakiegoś powodu nie zostały uwzględnione w węźle. Nowy katalog pojawił się w moim folderze node_modules, który sam w katalogu głównym znajduje się obok app.js. Następnie upewniłem się, że dodałem btoa-atob jako zależność w moim pliku package.json, który znajduje się w katalogu głównym.

Jednak z jakiegoś powodu nadal nie będzie działać.

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

^ powinien wypisać na konsoli komunikat „SGVsbG8gV29ybGQh”, ale zamiast tego pojawia się błąd „btoa nie jest zdefiniowany”.

Czy instalacja nie przebiegła prawidłowo? Co przeoczyłem?

Odpowiedzi:


537

Moduł „btoa-atob” nie eksportuje interfejsu programistycznego, udostępnia jedynie narzędzia wiersza poleceń.

Jeśli chcesz przekonwertować na Base64, możesz to zrobić za pomocą bufora:

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

Odwróć (zakładając, że treść, którą dekodujesz, jest ciągiem utf8):

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

Uwaga: przed Node v4 użyj new Bufferraczej niż Buffer.from.


57

Rozwiązania zamieszczone tutaj nie działają w przypadku znaków innych niż ascii (tj. Jeśli planujesz wymianę base64 między Node.js a przeglądarką). Aby to zadziałało, musisz zaznaczyć tekst wejściowy jako „binarny”.

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

To daje ci SOlsbPMgd/NybGQhIQ==. Jeśli zrobisz to atob('SOlsbPMgd/NybGQhIQ==')w przeglądarce, dekoduje ją we właściwy sposób. Zrobi to dobrze również w Node.js poprzez:

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

Jeśli nie wykonasz „części binarnej”, źle odszyfrujesz znaki specjalne.

Mam go z implementacji pakietu btoa npm :


1
dziękuję, oszalałem ze zmienionych postaci.
Matthew James Briggs,

1
Dziękuję Ivan, spędziłbym w tym godziny ... odpowiedź powinna być zaakceptowana!
Paweł

Iván Alegre Po prostu nie używaj kodowania „binarnego”. Jeśli to zrobisz Buffer.from('Hélló wórld!!').toString('base64')- da ci to, SOlsbPMgd/NybGQhIQ==co można poprawnie przekonwertować na łańcuch nie-ascii.
TotalAMD

1
@TotalAMD nie będzie działać wymiana base64 z Node.js na przeglądarkę lub viceversa
Iván Alegre

3
Porównujesz kodowanie w base64 i dekodujesz je na tej samej platformie. Chrome na Chrome i Node to Node. Jeśli kodujesz go w Węźle 10 bez pliku binarnego, da to SMOpbGzDsyB3w7NybGQhIQ==. Jeśli zdekodujesz to w przeglądarce, da ci to Hélló wórld!!. Plik binarny jest idealny do zapewnienia zgodności między platformami.
Iván Alegre

22

Mój zespół napotkał ten problem podczas używania Node z React Native i PouchDB. Oto jak to rozwiązaliśmy ...

Bufor instalacji NPM:

$ npm install --save buffer

Upewnij się Buffer, btoai atobsą ładowane jako globals:

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
Polecenie new Buffer () w kodzie powoduje następujący błąd w nowszych wersjach węzła: [DEP0005] DeprecationWarning: Buffer () jest nieaktualny z powodu problemów z bezpieczeństwem i użytecznością. Zamiast tego użyj metod Buffer.alloc (), Buffer.allocUnsafe () lub Buffer.from ().
Rodrigo De Almeida Siqueira

8

Przekonałem się, że chociaż podkładki z powyższych odpowiedzi działały, nie pasowały one do zachowań implementacji przeglądarek komputerowych btoa()i 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])));

Jak się okazuje, Bufferinstancje domyślnie reprezentują / interpretują ciągi kodowane w UTF-8 . Z kolei w przeglądarce Chrome na komputerze nie można nawet wprowadzić ciągu zawierającego znaki spoza zakresu Latin1btoa() , ponieważ spowoduje to wyjątek:Uncaught DOMException: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.

Dlatego musisz jawnie ustawić typ kodowania na latin1, aby podkładka Node.js była zgodna z typem kodowania komputera stacjonarnego 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])));

w węźle v0.12.2 nie ma funkcji
Buffer. z

@Zibri Node v0.12.2 jest starożytny i osiągnął koniec życia dwa lata temu . Buffer.from()jest zalecanym sposobem korzystania z interfejsu API bufora ze względów bezpieczeństwa (chociaż ten link wyjaśni alternatywne rozwiązania Buffer.from()dla Node v0.12.2).
Jamie Birch,

Rozumiem to, ale na wbudowanym urządzeniu mam tę wersję.
Zibri

Korzystam z kodu w Atomie, używając pakietu skryptu github.com/rgbkrk/atom-script, który jest starą implementacją węzła. Innymi słowy, potrzebuje także implementacji dla btoa, podczas gdy nie radzi sobie z Buffer.from ().
Shrimpy

4

Mam kod współdzielony między serwerem a klientem i potrzebowałem w nim implementacji btoa. Próbowałem zrobić coś takiego:

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

ale serwer zmiażdżyłby się:

ReferenceError: btoa nie jest zdefiniowany

podczas gdy Buffernie jest zdefiniowany na kliencie.

Nie mogłem sprawdzić window.btoa (to wspólny kod, pamiętasz?)

Skończyło się na tym wdrożeniu:

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

1

Rozumiem, że jest to punkt dyskusji dla aplikacji węzłowej, ale w interesie uniwersalnych aplikacji JavaScript działających na serwerze węzłów, i tak doszedłem do tego postu, badałem to pod kątem uniwersalnej / izomorficznej aplikacji reagującej, w której byłem budynku, a pakiet ababdziałał dla mnie. W rzeczywistości było to jedyne rozwiązanie, które udało mi się znaleźć, zamiast zadziałania wspomnianej metody buforowania (miałem problemy ze skryptami).

(Ten pakiet jest używany przez jsdom, który z kolei jest używany przez windowpakiet.)

Wracając do mojego punktu; na tej podstawie, być może jeśli ta funkcja jest już napisana jako pakiet npm, taki jak ten, o którym wspomniałeś, i ma swój własny algorytm oparty na specyfikacji W3, możesz zainstalować i używać tego ababpakietu, zamiast pisać własną funkcję, która może, ale nie musi, być dokładne oparte na kodowaniu.

--- EDYCJA ---

Zacząłem mieć dzisiaj dziwne problemy z kodowaniem (nie jestem pewien, dlaczego tak się teraz dzieje) z pakietem abab. Wydaje się, że przez większość czasu poprawnie koduje, ale czasami z przodu koduje niepoprawnie. Sporo czasu poświęciłem na debugowanie, ale przełączyłem się na pakiet base-64zgodnie z zaleceniami i od razu zadziałało. Zdecydowanie wydawało się, że sprowadza się to do algorytmu base64 z abab.


1

Ten sam problem z wtyczką „skryptową” w edytorze Atom, która jest starą wersją węzła, nie mającą btoa (), ani atob (), ani nie obsługuje typu danych Buffer. Poniższy kod rozwiązuje problem:

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


Działa dzięki. W moim przypadku używam ChakraEngine, który nie obsługuje atob.
Woda


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

Oto zwięzłe uniwersalne rozwiązanie dla kodowania base64:

const nodeBtoa = (b) => Buffer.from(b).toString('base64');
export const base64encode = typeof btoa !== 'undefined' ? btoa : nodeBtoa;
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.