【问题标题】:ArrayBuffer to base64 encoded stringArrayBuffer 到 base64 编码的字符串
【发布时间】:2012-03-05 06:58:13
【问题描述】:

我需要一种有效的(读取本机)方法将ArrayBuffer 转换为需要在多部分帖子中使用的 base64 字符串。

【问题讨论】:

    标签: javascript encoding base64 arraybuffer


    【解决方案1】:
    function _arrayBufferToBase64( buffer ) {
        var binary = '';
        var bytes = new Uint8Array( buffer );
        var len = bytes.byteLength;
        for (var i = 0; i < len; i++) {
            binary += String.fromCharCode( bytes[ i ] );
        }
        return window.btoa( binary );
    }
    

    但是,非本地实现更快,例如https://gist.github.com/958841http://jsperf.com/encoding-xhr-image-data/6

    【讨论】:

    • 我尝试了链接中的非本地实现,转换一个 1M 大小的缓冲区需要 1 分半钟,而上面的循环代码只需要 1 秒。
    • 我喜欢这种方法的简单性,但所有字符串连接都可能代价高昂。看起来在 Firefox、IE 和 Safari 上构建一个字符数组并在最后join()ing 他们明显更快(但在 Chrome 上慢得多):jsperf.com/tobase64-implementations
    • 我正在尝试使用 angualrjs 和 webapi2 上传 50mb 的 pdf 文件。我正在使用上面的代码,上传文件后,页面崩溃并挂起。下面的代码行,我被使用但在 webapi 方法中获取空值。 “var base64String = btoa(String.fromCharCode.apply(null, new Uint8Array(arrayBuffer)));”请提出任何想法...
    • 我想知道为什么每个人都避免使用本机缓冲区toString('base64') 方法。
    • @JoãoEduardoSoareseSilva 因为不是每个人都在使用 Node - Node 的 Buffer 在浏览器中不存在。
    【解决方案2】:

    这对我来说很好用:

    var base64String = btoa(String.fromCharCode.apply(null, new Uint8Array(arrayBuffer)));
    

    在 ES6 中,语法稍微简单一些:

    const base64String = btoa(String.fromCharCode(...new Uint8Array(arrayBuffer)));
    

    正如 cmets 中所指出的,当ArrayBuffer 很大时,此方法可能会在某些浏览器中导致运行时错误。在任何情况下,确切的大小限制都取决于实现。

    【讨论】:

    • 为了简洁起见,我更喜欢这种方法,但会出现“超出最大调用堆栈大小错误”。上面的循环技术解决了这个问题。
    • 我也遇到了堆栈大小错误,所以我使用了 mobz 的答案,效果很好。
    • 它不适用于大缓冲区。稍作修改使其工作:btoa([].reduce.call(new Uint8Array(bufferArray),function(p,c){return p+String.fromCharCode(c)},''))
    • 我正在尝试使用 angualrjs 和 webapi2 上传 50mb 的 pdf 文件。我正在使用上面的代码,上传文件后,页面崩溃并挂起。下面的代码行,我被使用但在 webapi 方法中得到空值。 “var base64String = btoa(String.fromCharCode.apply(null, new Uint8Array(arrayBuffer)));”请提出任何想法...
    • @Kugel btoa 对于代码范围 0-255 中的字符是安全的,因为这里就是这种情况(想想Uint8Array 中的 8)。
    【解决方案3】:

    对于喜欢简短的人来说,这是另一个使用Array.reduce 不会导致堆栈溢出的:

    var base64 = btoa(
      new Uint8Array(arrayBuffer)
        .reduce((data, byte) => data + String.fromCharCode(byte), '')
    );
    

    【讨论】:

    • 不确定这是否真的很性感。毕竟,您正在创建 &lt;amount of Bytes in the buffer&gt; 新字符串。
    • btoa(new Uint8Array(arraybuffer).reduce((data,byte)=&gt;(data.push(String.fromCharCode(byte)),data),[]).join('')) 怎么样?
    • 另一种选择:btoa(Array.from(new Uint8Array(arraybuffer)).map(b =&gt; String.fromCharCode(b)).join('')).
    • 在 'Window' 上执行 'btoa' 失败:要编码的字符串包含 Latin1 范围之外的字符
    【解决方案4】:

    还有另一种异步方式使用 Blob 和 FileReader。

    我没有测试性能。但这是一种不同的思维方式。

    function arrayBufferToBase64( buffer, callback ) {
        var blob = new Blob([buffer],{type:'application/octet-binary'});
        var reader = new FileReader();
        reader.onload = function(evt){
            var dataurl = evt.target.result;
            callback(dataurl.substr(dataurl.indexOf(',')+1));
        };
        reader.readAsDataURL(blob);
    }
    
    //example:
    var buf = new Uint8Array([11,22,33]);
    arrayBufferToBase64(buf, console.log.bind(console)); //"CxYh"
    

    【讨论】:

    • 使用dataurl.split(',', 2)[1]而不是dataurl.substr(dataurl.indexOf(',')+1)
    • 这似乎不能保证有效。根据w3c.github.io/FileAPI/#issue-f80bda5breadAsDataURL理论上可以返回一个百分比编码的dataURI(看起来jsdom实际上是这种情况)
    • @CarterMedlin 为什么split 会比substring 更好?
    • 分割更短。但是dataurl可能包含一个或多个逗号(,),拆分是不安全的。
    【解决方案5】:
    var blob = new Blob([arrayBuffer])
    
    var reader = new FileReader();
    reader.onload = function(event){
       var base64 =   event.target.result
    };
    
    reader.readAsDataURL(blob);
    

    【讨论】:

    • 请为您的答案添加一些解释。这段代码是什么意思?
    • 这是迄今为止最快的方法 - 在我有限的测试中比其他方法快几十倍
    • 我希望我能像 8 小时一样再次找到这个解决方案。我的一天不会被浪费;(谢谢
    • 我认为您还需要删除 DataURL 标头 (data:*/*;base64,) 才能仅获取 Base64 字符串。见MDN docs
    【解决方案6】:

    我用过这个并为我工作。

    function arrayBufferToBase64( buffer ) {
        var binary = '';
        var bytes = new Uint8Array( buffer );
        var len = bytes.byteLength;
        for (var i = 0; i < len; i++) {
            binary += String.fromCharCode( bytes[ i ] );
        }
        return window.btoa( binary );
    }
    
    
    
    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;
    }
    

    【讨论】:

    • 不安全。见@chemoish 答案
    • 这是我发现的唯一对我有用的解决方案。
    【解决方案7】:

    我的建议是不要使用原生 btoa 策略,因为它们不能正确编码所有 ArrayBuffer 的...

    rewrite the DOMs atob() and btoa()

    由于 DOMStrings 是 16 位编码的字符串,在大多数浏览器中,如果字符超出 8 位 ASCII 编码字符的范围,则对 Unicode 字符串调用 window.btoa 会导致字符超出范围异常。

    虽然我从未遇到过这个确切的错误,但我发现我尝试编码的许多 ArrayBuffer 编码不正确。

    我会使用 MDN 推荐或要点。

    【讨论】:

    • btoa 不适用于字符串,但 OP 正在询问 ArrayBuffer
    • 非常这个,这里这么多sn-ps推荐错了!我多次看到这个错误,人们盲目地使用 atob 和 btoa。
    • 所有数组缓冲区都应使用其他答案中的策略进行良好编码,atob/btoa 仅对包含大于 0xFF 的字符的文本存在问题(根据定义,字节数组不存在)。 MDN 警告不适用,因为在其他答案中使用该策略时,您可以保证有一个仅由 ASCII 字符组成的字符串,因为 Uint8Array 中的任何值都保证在 0 到 255 之间,这意味着保证 String.fromCharCode返回一个不超出范围的字符。
    • 这是当 btoa 或 Buffer 不可用时的正确答案(react-native)
    【解决方案8】:

    下面是 2 个简单的函数,用于将 Uint8Array 转换为 Base64 字符串并再次返回

    arrayToBase64String(a) {
        return btoa(String.fromCharCode(...a));
    }
    
    base64StringToArray(s) {
        let asciiString = atob(s);
        return new Uint8Array([...asciiString].map(char => char.charCodeAt(0)));
    }
    

    【讨论】:

    • 这是一个令人困惑的答案。这看起来不像是有效的 JavaScript,Uint8Array 是 ArrayBuffer 吗?
    • @user1153660 添加function 关键字,它应该可以在现代浏览器中使用。
    • 太棒了! btoa(String.fromCharCode(...a));是迄今为止我见过的对 Uint8Array 进行编码的最短版本。
    • 这看起来不错,但如果数组太大,则会抛出超出最大调用堆栈大小的错误。
    【解决方案9】:

    OP 没有指定运行环境,但是如果您使用的是 Node.JS,那么有一种非常简单的方法可以做到这一点。

    与官方 Node.JS 文档一致 https://nodejs.org/api/buffer.html#buffer_buffers_and_character_encodings

    // This step is only necessary if you don't already have a Buffer Object
    const buffer = Buffer.from(yourArrayBuffer);
    
    const base64String = buffer.toString('base64');
    

    此外,例如,如果您在 Angular 下运行,缓冲区类也将在浏览器环境中可用。

    【讨论】:

    • 你的回答只适用于NodeJS,在浏览器中不起作用。
    • @jvatic 我明白了,OP没有明确指定运行环境,所以我的回答没有错误,他只标记了Javascript。所以我更新了我的答案以使其更简洁。我认为这是一个重要的答案,因为我一直在寻找如何做到这一点,但无法找到问题的最佳答案。
    【解决方案10】:

    如果您可以添加库,base64-arraybuffer:

    yarn add base64-arraybuffer
    

    然后:

    • encode(buffer) - 将 ArrayBuffer 编码为 base64 字符串
    • decode(str) - 将 base64 字符串解码为 ArrayBuffer

    【讨论】:

    • 对我来说最好的答案,因为包括解码
    【解决方案11】:

    您可以使用Array.prototype.sliceArrayBuffer 派生一个普通数组。 使用Array.prototype.map之类的函数将字节转换为字符,然后join将它们组合在一起形成字符串。

    function arrayBufferToBase64(ab){
    
        var dView = new Uint8Array(ab);   //Get a byte view        
    
        var arr = Array.prototype.slice.call(dView); //Create a normal array        
    
        var arr1 = arr.map(function(item){        
          return String.fromCharCode(item);    //Convert
        });
    
        return window.btoa(arr1.join(''));   //Form a string
    
    }
    

    此方法更快,因为其中没有运行字符串连接。

    【讨论】:

    • 不安全。见@chemoish 答案
    【解决方案12】:

    在浏览器和Node.js中使用uint8-to-b64包进行编码/解码

    【讨论】:

      【解决方案13】:

      在我身边,使用 Chrome 导航器,我不得不使用 DataView() 来读取 arrayBuffer

      function _arrayBufferToBase64( tabU8A ) {
      var binary = '';
      let lecteur_de_donnees = new DataView(tabU8A);
      var len = lecteur_de_donnees.byteLength;
      var chaine = '';
      var pos1;
      for (var i = 0; i < len; i++) {
          binary += String.fromCharCode( lecteur_de_donnees.getUint8( i ) );
      }
      chaine = window.btoa( binary )
      return chaine;}
      

      【讨论】:

        【解决方案14】:
        function _arrayBufferToBase64(uarr) {
            var strings = [], chunksize = 0xffff;
            var len = uarr.length;
        
            for (var i = 0; i * chunksize < len; i++){
                strings.push(String.fromCharCode.apply(null, uarr.subarray(i * chunksize, (i + 1) * chunksize)));
            }
        
            return strings.join("");
        }
        

        如果你使用 JSZip 从字符串中解压存档,这样会更好

        【讨论】:

          猜你喜欢
          • 2011-04-22
          • 2014-03-14
          • 1970-01-01
          • 1970-01-01
          • 2018-08-15
          • 1970-01-01
          • 2018-12-28
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多