ArrayBuffer 到 base64 编码的字符串

Posted

技术标签:

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

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

【问题讨论】:

【参考方案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/958841 见http://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);

【讨论】:

请为您的答案添加一些解释。这段代码是什么意思? 类似helpful MDN example。 这是迄今为止最快的方法 - 在我有限的测试中比其他方法快几十倍 我希望我能像 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 推荐或要点。

https://github.com/beatgammit/base64-js https://gist.github.com/jonleighton/958841

【讨论】:

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 从字符串中解压存档,这样会更好

【讨论】:

以上是关于ArrayBuffer 到 base64 编码的字符串的主要内容,如果未能解决你的问题,请参考以下文章

uniapp解决图形验证码问题及arraybuffer二进制转base64格式图片

Base64编码及iOS中的Base64

微信小程序 base64ToArrayBuffer

将 base64 编码图像添加到 Microsoft Word 文档

将 base64 编码图像保存到 Firebase 存储

如何将 base64 编码的图像保存到磁盘?