将 base64字符串转换为 ArrayBuffer

我需要将 base64编码字符串转换为 ArrayBuffer。 Base64字符串是用户输入,它们将从电子邮件中复制和粘贴,所以当页面加载时它们不在那里。 如果可能的话,我希望在不对服务器进行 ajax 调用的情况下使用 javascript 完成此操作。

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

将 ArrayBuffer 转换为 base64编码的字符串

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

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

看起来不错,但是我不知道怎么用密码。

有没有一种简单的(也许是本地的)转换方法? 谢谢

249871 次浏览

试试这个:

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

由于 javascript-https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding中的 unicode 问题,它的回答不起作用。 < br/>
最后,我使用了 Daniel Guerrero 博客上给出的函数: “一个 ref =”http://blog.danguer.com/2011/10/24/base64-binary- 解码-in-javascript/”rel = “ norefrer”> http://blog.danguer.com/2011/10/24/base64-binary-decoding-in-javascript/

函数列在 github 链接上: < a href = “ https://github.com/danguer/blog-example/blob/master/js/base64-binary.js”rel = “ norefrer”> https://github.com/danguer/blog-examples/blob/master/js/base64-binary.js

使用以下行 < br/>

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

使用 字体数组:

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

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

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

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

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

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

如果基64字符串格式不正确,则发送请求失败。

Mime 类型(application/octet)可能是不必要的。

测试在铬。应该工作在其他浏览器。

Async 解决方案,数据量大时效果更好:

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

对于 Node.js 用户:

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

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

纯 JS-没有字符串中间步(没有 atob)

我编写了以下函数,直接转换 base64(中间步骤不转换为字符串)

  • 得到4个基本64字符块
  • 以64字母表为基数查找每个字符的索引
  • 将索引转换为6位数(二进制字符串)
  • 连接四个6位数,给出24位数(存储为二进制字符串)
  • 将24位字符串拆分为三个8位字符串,并将每个字符串转换为数字,并将它们存储在输出数组中
  • 角点大小写: 如果输入 base64字符串以一个/两个 =字符结束,则从输出数组中删除一个/两个数字

下面的解决方案允许处理大型输入 base64字符串

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

小心!

如果你想把 base64解码为 STRING (而不是字节数组) ,并且你知道结果包含 utf8字符,那么 atob通常会 失败,例如,对于字符,atob("8J+SqQ==")会给出错误的结果。在这种情况下,您可以使用上述解决方案,并在 正确的方式中将结果字节数组转换为字符串,例如:

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 testB64 = "8J+SqQ==";   // for string: "💩";
console.log('input base64            :', testB64);


let decodedBytes = base64ToBytesArr(testB64); // decode base64 to array of bytes
console.log('decoded bytes           :', JSON.stringify(decodedBytes));


let result = new TextDecoder("utf-8").decode(new Uint8Array(decodedBytes));
console.log('properly decoded string :', result);


let result_atob = atob(testB64);
console.log('decoded by atob         :', result_atob);

铬合金103.0.5060.134(arm64) ,狩猎旅行15.2,火狐103.0.1(64位) ,边缘103.0.1264.77(arm64)和 Node-js v12.16.1上测试的2022-08-04片段

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

我知道的最好的是 Rfc4648

问题是 btoa 和 atob 使用二进制字符串而不是 Uint8Array,并且尝试在它之间进行转换非常麻烦。而且在 npm 中也有坏包的 很多。我花了很多时间才找到那个。

这个特定包的创建者做了一件简单的事情: 他们使用 Base64(顺便说一句,它是 给你)的规范,并从头到尾正确地实现它。(包括规范中的其他格式,比如 Base64-url、 Base32等等也很有用... ...)这看起来并不多,但显然对其他库来说要求太多了。

所以,是的,我知道我正在做一点改宗,但如果你想避免浪费你的时间也只是使用 rfc4648。

Atob 的结果是一个用某个 逗号分隔的字符串

,

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

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

我使用这个问题的公认答案在通过 ASCII-cookie 传输的 base64Url 数据领域创建 base64Url string <-> arrayBuffer 转换[ atob,btoa are base64[ with +/] <-> js 二进制字符串] ,因此我决定发布代码。

我们中的许多人可能希望同时使用转换和客户机-服务器通信可能使用 base64Url 版本(尽管 cookie 可能包含 +/以及-_ 字符,如果我理解得很好,只有”; 字符和一些来自128 ASCII 的邪恶字符是不允许的)。但是 url 不能包含/字符,因此 b64 url 版本的广泛使用当然不是 atob-btoa 所支持的..。

看到其他的评论,我想强调一下,我在这里的用例是通过 url/cookie 的 base64Url 数据传输,并试图将这个加密数据与 js crypto api (2017)一起使用,因此需要 ArrayBuffer 表示和 b64u <-> arrBuff 转换... 如果数组缓冲区表示 base64以外的东西(ascii 的一部分) ,这种转换不会工作,因为 atob,btoa 是有限的 ascii (128)。检查一下适当的转换器,如下所示:

这个 buff-> b64u 版本来自 Mathias Bynens 的 tweet,谢谢!他还写了一个 base64编码器/解码器: Https://github.com/mathiasbynens/base64

来自 java,它可能有助于理解 java byte []实际上是 js Int8Array (有符号 int)的代码,但我们在这里使用无符号版本 Uint8Array,因为 js 转换与它们一起工作。它们都是256位的,所以我们现在在 js 中称它为 byte [] ..。

代码来自一个模块类,这就是为什么静态。

//utility


/**
* Array buffer to base64Url string
* - arrBuff->byte[]->biStr->b64->b64u
* @param arrayBuffer
* @returns {string}
* @private
*/
static _arrayBufferToBase64Url(arrayBuffer) {
console.log('base64Url from array buffer:', arrayBuffer);


let base64Url = window.btoa(String.fromCodePoint(...new Uint8Array(arrayBuffer)));
base64Url = base64Url.replaceAll('+', '-');
base64Url = base64Url.replaceAll('/', '_');


console.log('base64Url:', base64Url);
return base64Url;
}


/**
* Base64Url string to array buffer
* - b64u->b64->biStr->byte[]->arrBuff
* @param base64Url
* @returns {ArrayBufferLike}
* @private
*/
static _base64UrlToArrayBuffer(base64Url) {
console.log('array buffer from base64Url:', base64Url);


let base64 = base64Url.replaceAll('-', '+');
base64 = base64.replaceAll('_', '/');
const binaryString = window.atob(base64);
const length = binaryString.length;
const bytes = new Uint8Array(length);
for (let i = 0; i < length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}


console.log('array buffer:', bytes.buffer);
return bytes.buffer;
}

从 base64制作了一个 ArrayBuffer:

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

我尝试使用以上代码,它的工作正常。

atob溶液

我看到很多人在回复中抱怨使用 atobbtoa。使用它们时要考虑到 一些问题

在关于 基地64的 MDN 页面中有一个不使用它们的解决方案。下面您可以找到将 base64字符串转换为从文档复制的 Uint8Array 的代码。

注意,下面的函数返回一个 Uint8Array。

function b64ToUint6(nChr) {
return nChr > 64 && nChr < 91
? nChr - 65
: nChr > 96 && nChr < 123
? nChr - 71
: nChr > 47 && nChr < 58
? nChr + 4
: nChr === 43
? 62
: nChr === 47
? 63
: 0;
}


function base64DecToArr(sBase64, nBlocksSize) {
const sB64Enc = sBase64.replace(/[^A-Za-z0-9+/]/g, "");
const nInLen = sB64Enc.length;
const nOutLen = nBlocksSize
? Math.ceil(((nInLen * 3 + 1) >> 2) / nBlocksSize) * nBlocksSize
: (nInLen * 3 + 1) >> 2;
const taBytes = new Uint8Array(nOutLen);


let nMod3;
let nMod4;
let nUint24 = 0;
let nOutIdx = 0;
for (let nInIdx = 0; nInIdx < nInLen; nInIdx++) {
nMod4 = nInIdx & 3;
nUint24 |= b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << (6 * (3 - nMod4));
if (nMod4 === 3 || nInLen - nInIdx === 1) {
nMod3 = 0;
while (nMod3 < 3 && nOutIdx < nOutLen) {
taBytes[nOutIdx] = (nUint24 >>> ((16 >>> nMod3) & 24)) & 255;
nMod3++;
nOutIdx++;
}
nUint24 = 0;
}
}


return taBytes;
}

如果您对反向操作 ArrayBuffer to base64感兴趣,您可以在同一个链接中找到如何执行该操作。