Node.js видає помилку "btoa не визначено"


230

У моєму додатку node.js я зробив npm install btoa-atob так, що я міг використовувати функції btoa () та atob (), які є власними у javascript на стороні клієнта, але чомусь не були включені до вузла. Новий каталог з’явився в моїй папці node_modules, яка сама знаходиться в корені поряд з app.js. Тоді я переконався додати btoa-atob як залежність у своєму файлі package.json, який знаходиться в root.

Однак чомусь це все одно не вийде.

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

^ повинен вивести "SGVsbG8gV29ybGQh" на консоль, але замість цього я отримаю помилку "btoa не визначено."

Хіба я не зробив установку належним чином? Що я не помітив?

Відповіді:


537

Модуль 'btoa-atob' не експортує програмний інтерфейс, він лише надає утиліти командного рядка.

Якщо вам потрібно перетворити на Base64, ви можете зробити це за допомогою буфера:

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

Зворотний (якщо припустити, що вміст, який ви декодуєте, це рядок utf8):

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

Примітка: перед Node v4 використовуйте, new Bufferа не Buffer.from.


57

Розміщені тут рішення не працюють у символах, що не належать ascii (тобто якщо ви плануєте обміняти base64 між Node.js та браузером). Для того, щоб він працював, потрібно позначити вхідний текст як «двійковий».

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

Це дає вам SOlsbPMgd/NybGQhIQ==. Якщо ви робите atob('SOlsbPMgd/NybGQhIQ==')в браузері, він розшифрує його правильним чином. Це зробить це правильно також у Node.js через:

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

Якщо ви не виконаєте "бінарну частину", ви неправильно розшифруєте спеціальні символи.

Я отримав це з реалізації пакету btoa npm :


1
дякую, я зійшов з розуму зі зміненими персонажами.
— Меттью Джеймса Бріггса

1
Дякую тобі Іване, я б провів години на цьому ... ти відповідь повинен бути прийнятим!
— Pawel

Іван Алегре Просто не використовуйте «двійкове» кодування. Якщо ви це зробите Buffer.from('Hélló wórld!!').toString('base64')- це дасть вам SOlsbPMgd/NybGQhIQ==можливість повернення належним чином до рядка non-ascii
— TotalAMD

1
@TotalAMD не працюватиме обміном base64 з Node.js на браузер чи віверверсу
— Iván Alegre

3
Ви порівнюєте кодування в base64 і декодуєте його на одній платформі. Chrome to Chrome і Node to Node. Якщо ви кодуєте його у Вузлі 10 без двійкових даних, він дасть SMOpbGzDsyB3w7NybGQhIQ==. Якщо ви розшифруєте це в браузері, він видасть вам Hélló wórld!!. Бінарний файл ідеально підходить для забезпечення сумісності між платформами.
— Іван Алегре

22

Моя команда зіткнулася з цією проблемою, коли використовувала Node з React Native та PouchDB. Ось як ми це вирішили ...

Буфер встановлення NPM:

$ npm install --save buffer

Переконайтеся Buffer, btoaі atobзавантажуються в якості глобал:

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
Команда new Buffer () у вашому коді дає наступну помилку в новіших версіях вузла: [DEP0005] DeprecationWarning: Buffer () застаріле через проблеми безпеки та зручності використання. Будь ласка, замість цього використовуйте методи Buffer.alloc (), Buffer.allocUnsafe () або Buffer.from ().
— Родріго Де Альмейда Сікейра

8

Я виявив, що, хоч відповіді на відповіді вище працювали, вони не відповідали поведінці реалізацій настільних браузерів btoa()та 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])));

Як виявляється, Bufferекземпляри представляють / інтерпретують рядки, закодовані в UTF-8 за замовчуванням . Навпаки, у настільному Chrome ви навіть не можете вводити рядок, що містить символи, що не входять до діапазону latin1 btoa(), оскільки це призведе до виключення:Uncaught DOMException: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.

Тому вам потрібно чітко встановити тип кодування, щоб latin1ваш Node.js shim відповідав типу кодування настільного 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])));

у вузлі v0.12.2 немає функції
— Buffer.from

@Zibri Node v0.12.2 давньоруський і досяг кінця життя два роки тому . Buffer.from()є рекомендованим способом використання API буфера через причини безпеки (хоча це посилання уточнить альтернативи, Buffer.from()які можуть застосовуватися для Node v0.12.2).
— Джеймі Береза

Я це розумію, але на вбудованому пристрої у мене є ця версія.
— Зібрі

Я запускаю свій код в Atom, використовуючи пакет сценаріїв github.com/rgbkrk/atom-script, який є старою реалізацією вузла. Іншими словами, він також потребує реалізації для btoa, хоча він не може впоратися з Buffer.from ().
— Креветки

4

У мене код між сервером і клієнтом, і мені потрібна реалізація btoa всередині нього. Я спробував зробити щось на кшталт:

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

але Сервер розіб'ється з:

ReferenceError: btoa не визначено

поки Bufferне визначено для клієнта.

Не вдалося перевірити window.btoa (це загальний код, пам’ятаєте?)

Тому я закінчив цю реалізацію:

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

1

Я розумію, що це дискусійний пункт для вузлової програми, але в інтересах універсальних додатків JavaScript, що працюють на вузловому сервері, саме тому я прийшов на цю посаду, я досліджував це для програми універсального / ізоморфного реагування. будівлі, і пакет ababпрацював на мене. Насправді це було єдиним рішенням, яке я міг знайти, що працювало, а не використовуючи також метод Buffer, про який я також згадував (у мене були проблеми з машинописом).

(Цей пакет використовується компанією jsdom, яка в свою чергу використовується windowпакетом.)

Повертаючись до моєї точки зору; виходячи з цього, можливо, якщо ця функція вже написана як npm-пакет, як той, який ви згадали, і має власний алгоритм, заснований на W3, ви можете встановити та використовувати ababпакет, а не писати власну функцію, яка може бути, а може і не бути точні на основі кодування.

--- редагувати ---

Сьогодні у мене почалися дивні проблеми з кодуванням (не впевнений, чому це почалося зараз) з пакетом abab. Здається, вона кодує правильно більшу частину часу, але іноді на передньому кінці вона кодується неправильно. Тривалий час пробував налагоджувати, але перейшов на пакет, base-64як рекомендовано, і це спрацювало відразу. Очевидно, здавалося б, до алгоритму base64 abab.


1

Ця ж проблема з плагіном 'script' у редакторі Atom, який є старою версією вузла, не має btoa (), ні atob (), а також не підтримує тип даних Buffer. Наступний код робить фокус:

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


Це працює завдяки. У моєму випадку я використовую ChakraEngine, який, схоже, не підтримує atob.
— Вода


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

Ось стисле універсальне рішення для кодування base64:

const nodeBtoa = (b) => Buffer.from(b).toString('base64');
export const base64encode = typeof btoa !== 'undefined' ? btoa : nodeBtoa;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.