2012-10-03 121 views
46

我得到了一个webSocket通信,我收到base64编码的字符串,将其转换为uint8并在其上工作,但现在我需要发回,我得到了uint8数组,并且需要将其转换为base64字符串,因此我可以发送它。 我该如何做这个转换?如何将uint8数组转换为base64编码字符串?

+0

[MDN实现的用于Uint8array/ArrayBuffer <-> BASE64](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding#Solution_.232_.E2.80.93_rewriting_atob%28%29_and_btoa% 28%29_using_TypedArrays_and_UTF-8) – JLRishe

回答

106

如果您的浏览器已经TextDecoder然后使用:

var u8 = new Uint8Array([65, 66, 67, 68]); 
var decoder = new TextDecoder('utf8'); 
var b64encoded = btoa(decoder.decode(u8)); 

如果您需要支持browsers that do not have TextDecoder (目前只是IE和Edge),那么最好的选择是使用TextDecoder polyfill

如果你的字符串是纯ASCII和多字节不统一/ UTF-8则有是使用使用String.fromCharCode一个简单的替代方案,应普遍得到公平的支持:

var u8 = new Uint8Array([65, 66, 67, 68]); 
var b64encoded = btoa(String.fromCharCode.apply(null, u8)); 

而到了的base64字符串解码回到Uint8Array:

var u8_2 = new Uint8Array(atob(b64encoded).split("").map(function(c) { 
    return c.charCodeAt(0); })); 

如果你有非常大的数组缓冲区,则应用可能会失败,您可能需要块缓冲区(基于一个张贴@RohitSengar)。再次,注意,如果你的缓冲区只包含非多字节ASCII字符,这是唯一正确的:

function Uint8ToString(u8a){ 
    var CHUNK_SZ = 0x8000; 
    var c = []; 
    for (var i=0; i < u8a.length; i+=CHUNK_SZ) { 
    c.push(String.fromCharCode.apply(null, u8a.subarray(i, i+CHUNK_SZ))); 
    } 
    return c.join(""); 
} 
// Usage 
var u8 = new Uint8Array([65, 66, 67, 68]); 
var b64encoded = btoa(Uint8ToString(u8)); 
+4

这应该是公认的答案。 – jutky

+0

完美,它确实有效。 –

+3

这适用于我在Firefox中,但铬扼流圈与“未捕获RangeError:超过最大调用堆栈大小”(做btoa)。 –

-3

如果你想要的只是一个base64编码器的JS实现,所以你可以发回数据,你可以试试btoa函数。

b64enc = btoa(uint); 

关于btoa的一些快速注释 - 这是非标准的,所以浏览器不会强迫它支持它。 但是,大多数浏览器都这样。最重要的是,至少。 atob是相反的转换。

如果你需要一个不同的实现,或者你发现浏览器不知道你在说什么的边缘情况,寻找一个JS的base64编码器不会太难。

我觉得有他们的3挂在我公司的网站,出于某种原因...

+0

谢谢,我没有尝试过。 –

+10

几个音符。 btoa和atob实际上是HTML5标准化过程的一部分,而且大多数浏览器都以大致相同的方式支持它们。其次,btoa和atob只能使用字符串。在Uint8Array上运行btoa将首先使用toString()将缓冲区转换为字符串。这导致字符串“[object Uint8Array]”。这可能不是预期的。 – kanaka

+1

@CaioKeto你可能想考虑改变你选择的答案。这个答案不正确。 – kanaka

13
function Uint8ToBase64(u8Arr){ 
    var CHUNK_SIZE = 0x8000; //arbitrary number 
    var index = 0; 
    var length = u8Arr.length; 
    var result = ''; 
    var slice; 
    while (index < length) { 
    slice = u8Arr.subarray(index, Math.min(index + CHUNK_SIZE, length)); 
    result += String.fromCharCode.apply(null, slice); 
    index += CHUNK_SIZE; 
    } 
    return btoa(result); 
} 

如果你有一个非常大的Uint8Array,你可以使用这个函数。这是用于Javascript的,在FileReader的readAsArrayBuffer的情况下可以是有用的。

+2

有趣的是,在Chrome中,我在一个300kb的缓冲区中定时执行了这个操作,并发现像你这样做的区块会比逐字节地慢一点。这让我感到惊讶。 – Matt

+0

@Matt有趣。与此同时,Chrome可能会检测到此转换并对其进行了特定优化,并且数据分块可能会降低其效率。 – kanaka

+2

这不安全,是吗?如果我的块的边界穿过多字节的UTF8编码字符,那么[fromCharCode()](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/String/fromCharCode)将不会是能够从边界两侧的字节创建合理的字符,是吗? – Jens

7

非常简单的解决方案和JavaScript测试!

ToBase64 = function (u8) { 
    return btoa(String.fromCharCode.apply(null, u8)); 
} 

FromBase64 = function (str) { 
    return atob(str).split('').map(function (c) { return c.charCodeAt(0); }); 
} 

var u8 = new Uint8Array(256); 
for (var i = 0; i < 256; i++) 
    u8[i] = i; 

var b64 = ToBase64(u8); 
console.debug(b64); 
console.debug(FromBase64(b64)); 
+0

最干净的解决方案! – Abdel

0

我会添加另一个解决方案,使用不可打印的范围。我的猜测是这比链接TextEncoderbtoa快。

var blob = new Blob([ uint8ArrayBuffer ], { type: "image/jpeg" }); 
var imageUrl = URL.createObjectURL(blob); 

这是使用HTML5 API,所以当然不会在Node或其他基于JS的服务器上工作。你可以看到一个演示here

相关问题