【问题标题】:Node.js throws "btoa is not defined" errorNode.js 抛出“未定义 btoa”错误
【发布时间】:2014-05-30 15:30:41
【问题描述】:

在我的 node.js 应用程序中,我做了一个 npm install btoa-atob,以便我可以使用客户端 javascript 中原生的 btoa()atob() 函数,但由于某种原因未包含在 node.js 中。新目录出现在我的node_modules 文件夹中,该文件夹本身位于app.js 旁边的根目录中。然后我确保将 btoa-atob 作为依赖项添加到我的根目录中的package.json 文件中。

但是,由于某种原因,它仍然无法正常工作。

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

^ 应该将“SGVsbG8gV29ybGQh”输出到控制台,但我得到了错误:

btoa 未定义。

我没有正确安装吗?我忽略了什么?

【问题讨论】:

    标签: node.js


    【解决方案1】:

    “btoa-atob”模块不导出编程接口,它只提供命令行实用程序。

    如果您需要转换为 Base64,您可以使用 Buffer:

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

    反向(假设你正在解码的内容是一个 utf8 字符串):

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

    注意:在 Node v4 之前,使用 new Buffer 而不是 Buffer.from

    【讨论】:

      【解决方案2】:

      此处发布的解决方案不适用于非 ascii 字符(即,如果您计划在 Node.js 和浏览器之间交换 base64)。为了使其工作,您必须将输入文本标记为“二进制”。

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

      这会给你SOlsbPMgd/NybGQhIQ==。如果您在浏览器中创建atob('SOlsbPMgd/NybGQhIQ=='),它将以正确的方式对其进行解码。它也会在 Node.js 中通过:

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

      如果你不做“二进制部分”,你会错误地解码特殊字符。

      我知道了from the implementation of the btoa npm package:

      【讨论】:

      • Iván Alegre 只是不要使用“二进制”编码。如果你做Buffer.from('Hélló wórld!!').toString('base64')——它会给你SOlsbPMgd/NybGQhIQ==,它可以被正确地转换为非ascii字符串。
      • @TotalAMD 无法将 base64 从 Node.js 交换到浏览器,反之亦然
      • @IvánAlegre 刚刚检查过 a) 在 Chrome 75 中:atob(btoa("Hélló wórld!!")) === "Hélló wórld!!" b) 在节点 10 中:Buffer.from('Hélló wórld!!').toString('base64') === 'SMOpbGzDsyB3w7NybGQhIQ=='Buffer.from('SMOpbGzDsyB3w7NybGQhIQ==', 'base64').toString() === 'Hélló wórld!!' c) 即使删除尾随 '=',节点的 Buffer.from() 也可以读取,但您始终可以编写简单的函数来完成带有尾随 '=' 的编码字符串。
      • 您正在比较 base64 中的编码并在同一平台上对其进行解码。 Chrome 到 Chrome 和节点到节点。如果你在没有二进制的节点 10 中对其进行编码,它将给出SMOpbGzDsyB3w7NybGQhIQ==。如果您在浏览器中对此进行解码,它将为您提供Hélló wórld!!。该二进制文件非常适合确保跨平台兼容性。
      • 你在函数中的答案:function btoa(str){return Buffer.from(str, 'binary').toString('base64');}function atob(str){return Buffer.from(str, 'base64').toString('binary');}
      【解决方案3】:

      我的团队在使用带有 React Native 和 PouchDB 的 Node 时遇到了这个问题。以下是我们如何解决它...

      NPM 安装缓冲区:

      $ npm install --save buffer
      

      确保 Bufferbtoaatob 作为全局变量加载:

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

      【讨论】:

      • 代码中的命令 new Buffer() 在较新版本的节点中会出现以下错误:[DEP0005] DeprecationWarning: Buffer() 由于安全性和可用性问题而被弃用。请改用 Buffer.alloc()、Buffer.allocUnsafe() 或 Buffer.from() 方法。
      • @RodrigoDeAlmeidaSiqueira,您可以使用 Buffer.from() 修复警告:)
      【解决方案4】:

      我发现虽然上述答案中的填充程序有效,但它们与桌面浏览器的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 by default 编码的字符串。相比之下,在桌面版 Chrome 中,您甚至不能将包含 latin1 范围之外的字符的字符串输入到btoa(),因为它会抛出异常:Uncaught DOMException: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.

      因此,您需要将 encoding type 显式设置为 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 是古老的,已经到了生命周期的尽头two years ago。出于安全原因,Buffer.from() 是使用 Buffer API 的 recommended way(尽管该链接将阐明可能适用于 Node v0.12.2 的 Buffer.from() 的替代方案)。
      • 我明白,但在嵌入式设备上我有那个版本。
      • 我在 Atom 中使用脚本包 github.com/rgbkrk/atom-script 运行我的代码,这是一个旧的节点实现。换句话说,它也需要一个btoa的实现,而它不能处理Buffer.from()。
      • 我赞成这个,因为它最接近正确。浏览器的 atob / btoa 函数特别需要 0-255 范围内的字符码位。 Latin1 在此范围内,但不使用此范围内的每个字符。 btoa 和 atob 的重点是对实际二进制数据进行编码/解码,以便通过文本通道进行传输。如果您正在编码/解码文本,则 atob 和 btoa 可能与您正在做的事情无关。
      【解决方案5】:
      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();
        }
      };
      

      【讨论】:

      【解决方案6】:

      我有在服务器和客户端之间共享的代码,我需要在其中实现 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')
          }
      };
      

      【讨论】:

        【解决方案7】:

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

        【讨论】:

        • 谢谢。就我而言,我使用的是似乎不支持 atob 的 ChakraEngine。
        【解决方案8】:

        这里有一个简洁的base64编码通用解决方案:

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

        【讨论】:

          【解决方案9】:

          我能够使用btoa 使用以下 npm 包将二进制数据转换为 base 64 字符串: https://www.npmjs.com/package/btoa

          如他们的文档中所述,我在节点 JS 应用程序中执行了以下步骤:

          1. 安装 => npm install --save btoa
          2. 在顶部声明 => const btoa = require('btoa');
          3. 使用 => const b64 = btoa("stringToEncode");

          【讨论】:

            【解决方案10】:

            我知道这是一个节点应用程序的讨论点,但是为了通用 JavaScript 应用程序在节点服务器上运行,这就是我到达这篇文章的方式,我一直在研究这个对于我一直在构建的通用/同构反应应用程序,包abab 为我工作。事实上,这是我能找到的唯一可行的解​​决方案,而不是使用提到的 Buffer 方法(我遇到了打字稿问题)。

            (此包由jsdom 使用,而window 包又使用此包。)

            回到我的观点;基于此,也许如果这个功能已经像你提到的那样写成一个 npm 包,并且有它自己的基于 W3 规范的算法,你可以安装和使用 abab 包而不是编写你自己的函数可能或根据编码可能不准确。

            ---编辑---

            我今天开始在使用包abab 的编码方面遇到奇怪的问题(不知道为什么现在开始发生)。它似乎大部分时间都正确编码,但有时在前端它编码不正确。花了很长时间尝试调试,但按照推荐切换到包base-64,它立即工作。看来肯定是abab的base64算法了。

            【讨论】:

              【解决方案11】:

              任何想要解码的人:

              let decoded = Buffer.from(&lt;encoded string&gt;, 'base64').toString().

              因为我是来这里寻找解码的,所以最终从这里的答案中弄清楚了。

              【讨论】:

                猜你喜欢
                • 2014-07-09
                • 2018-11-15
                • 2019-04-19
                • 2016-12-14
                • 2016-02-11
                • 2013-02-18
                • 2014-09-21
                • 2012-04-14
                相关资源
                最近更新 更多