【问题标题】:Convert base64 string to ArrayBuffer将 base64 字符串转换为 ArrayBuffer
【发布时间】:2014-03-14 20:57:13
【问题描述】:

我需要将 base64 编码字符串转换为 ArrayBuffer。 base64 字符串是用户输入的,它们将从电子邮件中复制和粘贴,因此在加载页面时它们不存在。 如果可能的话,我想在 javascript 中执行此操作而不对服务器进行 ajax 调用。

我发现这些链接很有趣,但它们并没有帮助我:

ArrayBuffer to base64 encoded string

这是关于相反的转换,从 ArrayBuffer 到 base64,而不是相反

http://jsperf.com/json-vs-base64/2

这看起来不错,但我不知道如何使用代码。

是否有一种简单的(可能是原生的)方法来进行转换?谢谢

【问题讨论】:

    标签: javascript arrays base64 arraybuffer


    【解决方案1】:

    试试这个:

    function _base64ToArrayBuffer(base64) {
        var binary_string = window.atob(base64);
        var len = binary_string.length;
        var bytes = new Uint8Array(len);
        for (var i = 0; i < len; i++) {
            bytes[i] = binary_string.charCodeAt(i);
        }
        return bytes.buffer;
    }
    

    【讨论】:

    • 请解释一下这里到底发生了什么。
    • 这很简单,首先我们解码 base64 字符串 (atob),然后我们创建一个新的 8 位无符号整数数组,其长度与解码后的字符串相同。之后,我们迭代字符串并使用字符串中每个字符的 Unicode 值填充数组。
    • 来自 MDN:Base64 是一组类似的二进制到文本编码方案,通过将二进制数据转换为 radix-64 表示,以 ASCII 字符串格式表示二进制数据。 Uint8Array 类型数组表示一个 8 位无符号整数数组,我们正在处理数据的 ASCII 表示(这也是一个 8 位表)..
    • 这是不正确的。它允许 javascript 将字节解释为字符串,这会影响实际上是二进制的数据。
    • 问题在于 a) 不是每个字节序列都是有效的 unicode b) 不是 unicode 中的每个字符都是一个字节,所以bytes[i] = binary_string.charCodeAt(i); 可能是错误的
    【解决方案2】:

    使用TypedArray.from:

    Uint8Array.from(atob(base64_string), c => c.charCodeAt(0))
    

    与 Goran.it 答案的 for 循环版本进行比较的性能。

    【讨论】:

    • 喜欢这种单行本的朋友,请注意Uint8Array.from对部分浏览器的兼容性还是比较差的。
    • rails 编译器无法处理此字符串并以ExecJS::RuntimeError: SyntaxError: Unexpected token: operator (&gt;) 失败; (轨道 5)
    • 这不是数组缓冲区。这是类型化数组。您可以通过从 Uint8Array 返回的内容的 .buffer 属性访问数组缓冲区
    • @Saites,atobbtoa 没有任何问题,您只需给他们有效的输入。 atob 需要一个有效的 base64 字符串,否则会抛出错误。而btoa 需要一个有效的字节字符串(也称为二进制字符串),它是一个包含 0-255 范围内的字符的字符串。如果您的字符串包含超出该范围的字符,btoa 将引发错误。
    【解决方案3】:

    由于 javascript - https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding 中的 unicode 问题,Goran.it 的答案不起作用。

    我最终使用了 Daniel Guerrero 博客上给出的函数:http://blog.danguer.com/2011/10/24/base64-binary-decoding-in-javascript/

    函数列在 github 链接:https://github.com/danguer/blog-examples/blob/master/js/base64-binary.js

    使用这些行

    var uintArray = Base64Binary.decode(base64_string);  
    var byteArray = Base64Binary.decodeArrayBuffer(base64_string); 
    

    【讨论】:

    • 这种方法比使用 atob 快 2 倍。
    • 你能举一个不适用的例子吗?这篇文章讨论了编码任意字符串,其中可能包含 unicode 字符,但根本不适用于atob
    • decodeArrayBuffer 返回一个ArrayBuffer,其大小始终可以被 3 整除,我不明白这是设计使然还是错误。我会在github项目中问。
    • @ceztko 这可能是(意外)设计的。 base64 编码算法以 3 个字节为一组,并将它们转换为 4 个字符。 decode 方法可能会分配一个长度为 base64String.length/4*3 字节的 ArrayBuffer,并且在完成时从不截断任何未使用的字节。
    • @AlwaysLearning 这意味着它可能存在错误,因为剩余的零字节可能会破坏预期的输出内容。
    【解决方案4】:

    对于 Node.js 用户:

    const myBuffer = Buffer.from(someBase64String, 'base64');
    

    myBuffer 将是 Buffer 类型,它是 Uint8Array 的子类。不幸的是, Uint8Array 不是 OP 要求的 ArrayBuffer 。但是在操作 ArrayBuffer 时,我几乎总是用 Uint8Array 或类似的东西包装它,所以它应该接近要求的内容。

    【讨论】:

      【解决方案5】:

      刚刚发现 base64-arraybuffer,一个使用率非常高的小型 npm 包,上个月(2017-08)下载了 500 万次。

      https://www.npmjs.com/package/base64-arraybuffer

      对于任何寻找最佳标准解决方案的人来说,这可能就是它。

      【讨论】:

      • 我将它与音频 API 一起使用,它开箱即用。
      【解决方案6】:

      异步方案,数据大的时候比较好:

      // base64 to buffer
      function base64ToBufferAsync(base64) {
        var dataUrl = "data:application/octet-binary;base64," + base64;
      
        fetch(dataUrl)
          .then(res => res.arrayBuffer())
          .then(buffer => {
            console.log("base64 to buffer: " + new Uint8Array(buffer));
          })
      }
      
      // buffer to base64
      function bufferToBase64Async( buffer ) {
          var blob = new Blob([buffer], {type:'application/octet-binary'});    
          console.log("buffer to blob:" + blob)
      
          var fileReader = new FileReader();
          fileReader.onload = function() {
            var dataUrl = fileReader.result;
            console.log("blob to dataUrl: " + dataUrl);
      
            var base64 = dataUrl.substr(dataUrl.indexOf(',')+1)      
            console.log("dataUrl to base64: " + base64);
          };
          fileReader.readAsDataURL(blob);
      }
      

      【讨论】:

        【解决方案7】:

        Javascript 是一个很好的开发环境,所以它似乎很奇怪,它没有为这个小问题提供解决方案。本页其他地方提供的解决方案可能很慢。这是我的解决方案。它采用解码 base64 图像和声音数据 url 的内置功能。

        var req = new XMLHttpRequest;
        req.open('GET', "data:application/octet;base64," + base64Data);
        req.responseType = 'arraybuffer';
        req.onload = function fileLoaded(e)
        {
           var byteArray = new Uint8Array(e.target.response);
           // var shortArray = new Int16Array(e.target.response);
           // var unsignedShortArray = new Int16Array(e.target.response);
           // etc.
        }
        req.send();
        

        如果 base 64 字符串格式错误,则发送请求失败。

        mime 类型(应用程序/八位字节)可能是不必要的。

        在 chrome 中测试。应该可以在其他浏览器中使用。

        【讨论】:

        • 这对我来说是完美的解决方案,简单而干净。我很快在 Firefox、IE 11、Edge 中测试了它并且运行良好!
        • 我不确定它在 IE11 中如何为您工作,但我收到 Access Denied 错误,这似乎是 CORS 限制。
        • 这可以用 async/await 和 Fetch API 更简洁地写成 await (await fetch("data:application/octet;base64," + base64data)).arrayBuffer()
        【解决方案8】:

        纯 JS - 无字符串中间步骤(无 atob)

        我编写了以下函数,它以直接方式转换 base64(在中间步骤不转换为字符串)。想法

        • 获取 4 个 base64 字符块
        • 查找 base64 字母表中每个字符的索引
        • 将索引转换为 6 位数字(二进制​​字符串)
        • 连接四个 6 位数字,得到 24 位数字(存储为二进制字符串)
        • 将 24 位字符串拆分为三个 8 位字符串,并将每个字符串转换为编号并将它们存储在输出数组中
        • 极端情况:如果输入 base64 字符串以一/二 = 字符结尾,则从输出数组中删除一/二数字

        以下解决方案允许处理大型输入 base64 字符串。在没有 btoa 的情况下将字节转换为 base64 的类似函数是 HERE

        function base64ToBytesArr(str) {
          const abc = [..."ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"]; // base64 alphabet
          let result = [];
        
          for(let i=0; i<str.length/4; i++) {
            let chunk = [...str.slice(4*i,4*i+4)]
            let bin = chunk.map(x=> abc.indexOf(x).toString(2).padStart(6,0)).join(''); 
            let bytes = bin.match(/.{1,8}/g).map(x=> +('0b'+x));
            result.push(...bytes.slice(0,3 - (str[4*i+2]=="=") - (str[4*i+3]=="=")));
          }
          return result;
        }
        
        
        // --------
        // TEST
        // --------
        
        
        let test = "Alice's Adventure in Wonderland.";  
        
        console.log('test string:', test.length, test);
        let b64_btoa = btoa(test);
        console.log('encoded string:', b64_btoa);
        
        let decodedBytes = base64ToBytesArr(b64_btoa); // decode base64 to array of bytes
        console.log('decoded bytes:', JSON.stringify(decodedBytes));
        let decodedTest = decodedBytes.map(b => String.fromCharCode(b) ).join``;
        console.log('Uint8Array', JSON.stringify(new Uint8Array(decodedBytes)));
        console.log('decoded string:', decodedTest.length, decodedTest);

        【讨论】:

        • 所以没有遗漏“.”?
        • 在浏览器中测试,我不确定这是预期的结果吗? “爱丽丝梦游仙境”(即最后一个字符是 NaN)
        • @GillsoftAB 谢谢你的信息 - 你是对的 - 我解决了这个问题
        • @TefoD 上面的代码显示了输入和输出字符串的长度——我在少数情况下对其进行了测试——输入字符串的长度始终与输出字符串的长度相同。那么如何在输出端检测到尾随额外的 bx00 呢? (提供示例输入和检测问题的方法)
        • @KamilKiełczewski,对不起我的错 - 尾随的 0 零来自你之前的一个函数 - 我将删除我之前的废话评论。
        【解决方案9】:

        我强烈建议使用正确实现 base64 规范的 npm 包。

        我知道的最好的是rfc4648

        问题是 btoa 和 atob 使用二进制字符串而不是 Uint8Array 并且尝试与它相互转换很麻烦。在 npm 中也有 很多 的坏包。在找到那个之前我浪费了很多时间。

        该特定包的创建者做了一件简单的事情:他们采用 Base64 的规范(顺便说一下是here)并从头到尾正确实现它。 (包括规范中也有用的其他格式,如 Base64-url、Base32 等......)这似乎并不多,但显然这对其他库来说太多了。

        所以是的,我知道我在做一些传教,但如果你也想避免浪费时间,那就使用 rfc4648。

        【讨论】:

        • 这个库非常适合我的用例。感谢分享!
        【解决方案10】:

        atob 的结果是一个用一些逗号

        分隔的字符串

        ,

        更简单的方法是将此字符串转换为 json 数组字符串,然后将其解析为 byteArray 下面的代码可以简单地用于将base64转换为数字数组

        let byteArray = JSON.parse('['+atob(base64)+']'); 
        let buffer = new Uint8Array(byteArray);
        

        【讨论】:

          【解决方案11】:
          let str = "dGhpcyBpcyBiYXNlNjQgc3RyaW5n"
          let encoded = new TextEncoder().encode(str) // is Uint8Array
          let buf = encoded.buffer // is ArrayBuffer
          

          【讨论】:

          • 请注意,这不会执行任何 Base64 解码/编码。它只是将“base64”的 6 个字节转换为 6 元素的 ArrayBuffer 或 Uint8Array。
          • @dubek 这就是被问到的。
          猜你喜欢
          • 2021-12-25
          • 2021-12-12
          • 1970-01-01
          • 2016-08-11
          • 2014-05-10
          • 2017-07-28
          • 2021-12-13
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多