Node.js ném lỗi Btoa không được xác định lỗi


230

Trong ứng dụng node.js của tôi, tôi đã làm npm install btoa-atobnhư vậy để tôi có thể sử dụng các hàm btoa () và atob () có nguồn gốc trong javascript phía máy khách nhưng vì một số lý do không bao gồm trong nút. Thư mục mới xuất hiện trong thư mục node_modules của tôi, chính nó nằm trong root cùng với app.js. Sau đó, tôi đã đảm bảo thêm btoa-atob như một phần phụ thuộc trong tệp pack.json của mình trong thư mục gốc.

Tuy nhiên, vì một số lý do, nó vẫn không hoạt động.

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

^ nên xuất "SGVsbG8gV29ybGQh" cho bàn điều khiển, nhưng thay vào đó, tôi nhận được lỗi "btoa không được xác định."

Tôi đã không cài đặt đúng cách? Tôi đã bỏ qua những gì?

Câu trả lời:


537

Mô-đun 'btoa-atob' không xuất giao diện lập trình, nó chỉ cung cấp các tiện ích dòng lệnh.

Nếu bạn cần chuyển đổi sang Base64, bạn có thể làm như vậy bằng cách sử dụng Bộ đệm:

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

Đảo ngược (giả sử nội dung bạn đang giải mã là một chuỗi utf8):

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

Lưu ý: trước Node v4, hãy sử dụng new Bufferchứ không phải Buffer.from.


57

Các giải pháp được đăng ở đây không hoạt động với các ký tự không phải mã ascii (tức là nếu bạn có kế hoạch trao đổi base64 giữa Node.js và trình duyệt). Để làm cho nó hoạt động, bạn phải đánh dấu văn bản đầu vào là 'nhị phân'.

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

Điều này mang lại cho bạn SOlsbPMgd/NybGQhIQ==. Nếu bạn thực hiện atob('SOlsbPMgd/NybGQhIQ==')trong một trình duyệt, nó sẽ giải mã nó theo đúng cách. Nó cũng sẽ làm điều đó ngay trong Node.js thông qua:

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

Nếu bạn không thực hiện "phần nhị phân", bạn sẽ giải mã sai các ký tự đặc biệt.

Tôi đã nhận được nó từ việc thực hiện gói btoa npm :


1
cảm ơn bạn, tôi đã phát điên với các nhân vật thay đổi.
Matthew James Briggs

1
Cảm ơn bạn Ivan, tôi đã dành hàng giờ trong việc này ... bạn trả lời nên là người được chấp nhận!
Pawel

Iván Alegre Chỉ không sử dụng mã hóa 'nhị phân'. Nếu bạn làm thế Buffer.from('Hélló wórld!!').toString('base64')- nó sẽ cung cấp cho bạn SOlsbPMgd/NybGQhIQ==chuỗi có thể được chuyển đổi thành chuỗi không ascii trở lại đúng cách.
TotalAMD

1
@TotalAMD nó sẽ không hoạt động khi trao đổi base64 từ Node.js sang trình duyệt hoặc viceversa
Iván Alegre

3
Bạn đang so sánh mã hóa trong base64 và giải mã nó trong cùng một nền tảng. Chrome sang Chrome và Nút đến Nút. Nếu bạn mã hóa nó trong Nút 10 mà không có nhị phân, nó sẽ cung cấp SMOpbGzDsyB3w7NybGQhIQ==. Nếu bạn giải mã điều này trong một trình duyệt, nó sẽ cung cấp cho bạn Hélló wórld!!. Các nhị phân là hoàn hảo để đảm bảo khả năng tương thích đa nền tảng.
Iván Alegre

22

Nhóm của tôi gặp phải vấn đề này khi sử dụng Node với React Native và PouchDB. Đây là cách chúng tôi giải quyết nó ...

Bộ đệm cài đặt NPM:

$ npm install --save buffer

Đảm bảo Buffer, btoaatobđược tải dưới dạng toàn cầu:

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
Lệnh Buffer () mới trong mã của bạn đưa ra lỗi sau trong các phiên bản nút mới hơn: [DEP0005] DeprecationWarning: Buffer () không dùng nữa do vấn đề bảo mật và khả năng sử dụng. Vui lòng sử dụng các phương thức Buffer.alloc (), Buffer.allocUnsafe () hoặc Buffer.from () để thay thế.
Rodrigo De Almeida Siqueira

8

Tôi thấy rằng mặc dù các miếng chêm từ các câu trả lời ở trên đã hoạt động, nhưng chúng không khớp với hành vi triển khai của trình duyệt máy tính để bàn 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])));

Hóa ra, Buffer thể hiện đại diện / giải thích các chuỗi được mã hóa theo UTF-8 theo mặc định . Ngược lại, trong Chrome dành cho máy tính để bàn, bạn thậm chí không thể nhập một chuỗi có chứa các ký tự bên ngoài phạm vi latin1 btoa(), vì nó sẽ đưa ra một ngoại lệ:Uncaught DOMException: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.

Do đó, bạn cần đặt rõ ràng loại mã hóa thành latin1để shim Node.js của bạn khớp với loại mã hóa của Chrome trên máy tính để bàn:

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

trong nút v0.12.2 không có chức năng Buffer.from
Zibri

@Zibri Node v0.12.2 là cổ xưa và đạt đến cuối đời hai năm trước . Buffer.from()cách được đề xuất để sử dụng API bộ đệm vì lý do bảo mật (mặc dù liên kết đó sẽ làm rõ các lựa chọn thay thế Buffer.from()có thể áp dụng cho Node v0.12.2).
Jamie Birch

Tôi hiểu điều đó, nhưng trên một thiết bị nhúng tôi có phiên bản đó.
Zibri

Tôi đang chạy mã của mình trong Atom bằng gói tập lệnh github.com/rgbkrk/atom-script là một triển khai cũ của nút. Nói cách khác, nó cũng cần một triển khai cho btoa, trong khi nó không thể đối phó với Buffer.from ().
Tôm

4

Tôi có một mã được chia sẻ giữa máy chủ và máy khách và tôi cần triển khai btoa bên trong nó. Tôi đã thử làm một cái gì đó như:

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

nhưng Máy chủ sẽ nghiền nát với:

ReferenceError: btoa không được xác định

trong khi Bufferkhông được xác định trên máy khách

Tôi không thể kiểm tra window.btoa (đó là mã được chia sẻ, nhớ chứ?)

Vì vậy, tôi đã kết thúc với việc thực hiện này:

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

1

Tôi hiểu đây là một điểm thảo luận cho một ứng dụng nút, nhưng vì lợi ích của các ứng dụng JavaScript phổ biến đang chạy trên một máy chủ nút, đó là cách tôi đến bài viết này, tôi đã nghiên cứu ứng dụng này cho một ứng dụng phản ứng phổ biến / đẳng cấu xây dựng, và các gói abablàm việc cho tôi. Trong thực tế, đó là giải pháp duy nhất tôi có thể tìm thấy có hiệu quả, thay vì sử dụng phương thức Bộ đệm cũng được đề cập (tôi có vấn đề về bản in).

(Gói này được sử dụng bởi jsdom, lần lượt được sử dụng bởiwindow gói.)

Trở lại quan điểm của tôi; dựa trên điều này, có lẽ nếu chức năng này đã được viết dưới dạng gói npm giống như gói bạn đã đề cập và có thuật toán riêng dựa trên thông số W3, bạn có thể cài đặt và sử dụngabab gói thay vì viết chức năng của riêng bạn có thể có hoặc không chính xác dựa trên mã hóa.

--- CHỈNH SỬA ---

Hôm nay tôi bắt đầu gặp vấn đề kỳ lạ với mã hóa (không biết tại sao nó lại bắt đầu xảy ra bây giờ) với gói abab. Nó dường như mã hóa chính xác hầu hết thời gian, nhưng đôi khi ở mặt trước, nó mã hóa không chính xác. Đã dành một thời gian dài để cố gắng gỡ lỗi, nhưng chuyển sang gói base-64theo khuyến nghị và nó hoạt động ngay lập tức. Chắc chắn dường như là xuống thuật toán base64 của abab.


1

Vấn đề tương tự với plugin 'script' trong trình soạn thảo Atom, đây là phiên bản cũ của nút, không có btoa (), cũng không phải atob (), cũng không hỗ trợ kiểu dữ liệu Buffer. Mã sau đây thực hiện thủ thuật:

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


Điều này làm việc cảm ơn. Trong trường hợp của tôi, tôi đang sử dụng ChakraEngine dường như không hỗ trợ atob.
Nước


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

Đây là một giải pháp phổ quát ngắn gọn cho mã hóa base64:

const nodeBtoa = (b) => Buffer.from(b).toString('base64');
export const base64encode = typeof btoa !== 'undefined' ? btoa : nodeBtoa;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.