JavaScript實(shí)現(xiàn)ArrayBuffer到Base64的轉(zhuǎn)換
前置概念
- ArrayBuffer:就像一個(gè)巨大的數(shù)字水池,里面裝滿了 0 和 1。
- Base64:一種字符編碼格式,它用 64 個(gè)字符
(A-Z, a-z, 0-9, +, /)
?來(lái)表示數(shù)據(jù)。 - TextDecoder:就像一個(gè)神奇的翻譯器,能夠?qū)⑺乩锏臄?shù)字變成普通的文字。
- btoa:這個(gè)函數(shù)就像一個(gè)魔術(shù)師,能將普通文字變成 Base64 編碼。
問(wèn)題引入:將圖片數(shù)據(jù)轉(zhuǎn)為 Base64 時(shí)遇到意外
在前端開(kāi)發(fā)中,我們經(jīng)常需要處理從服務(wù)器獲取的圖片數(shù)據(jù)。有時(shí),我們需要將這些數(shù)據(jù)(ArrayBuffer )轉(zhuǎn)換為 Base64 格式,以便進(jìn)一步處理,或者進(jìn)一步向其他位置傳播。
通常我們會(huì)使用如下代碼:
const base64 = btoa(String.fromCharCode.apply(null, new Uint8Array(arrayBuffer)));
工作原理
new Uint8Array(arrayBuffer)
?:- 這一步將 ArrayBuffer 轉(zhuǎn)換為 Uint8Array。
- 可以將其想象為把一大桶水(ArrayBuffer)倒入一排整齊的小杯子(Uint8Array)中,每個(gè)杯子恰好裝 8 位(1字節(jié))的數(shù)據(jù)。
String.fromCharCode.apply(null, ...)
?:- ?
String.fromCharCode
? 是一個(gè)方法,它接受一系列 Unicode 值,并返回由這些值組成的字符串。 - ?
.apply(null, ...)
? 的作用是將 Uint8Array 中的所有元素作為獨(dú)立參數(shù)傳遞給String.fromCharCode
?。這就像是試圖一次性抓住所有的小水杯。
- ?
btoa(...)
?:- 最后,
btoa
? 函數(shù)將生成的字符串編碼為 Base64。
- 最后,
這種方法對(duì)于小型 ArrayBuffer 來(lái)說(shuō)非常高效,因?yàn)樗?jiǎn)潔且直接。
然而,當(dāng)我們嘗試將較大的圖片轉(zhuǎn)換為 Base64 字符串時(shí),這段代碼就會(huì)拋出以下錯(cuò)誤:
RangeError: Maximum call stack size exceeded
為什么會(huì)棧溢出呢,問(wèn)題出在 String.fromCharCode.apply()
? 方法上。當(dāng)處理大型 ArrayBuffer 時(shí),這種方法試圖一次性將所有數(shù)據(jù)作為參數(shù)傳遞給函數(shù),導(dǎo)致超出了 JavaScript 的調(diào)用棧限制。
想象你正在嘗試將一個(gè)巨大的拼圖(ArrayBuffer)快速組裝起來(lái):
- 首先,你把所有拼圖塊整齊地排列在桌上(創(chuàng)建 Uint8Array)。
- 然后,你試圖一次性抓起所有拼圖塊(
apply
? 方法),想要立即將它們組合成完整的圖像(String.fromCharCode
?)。 - 最后,你要給這幅拼好的圖像加上特殊的裝裱(
btoa
? 轉(zhuǎn)換為 Base64)。
問(wèn)題在于,當(dāng)拼圖太大時(shí),你的手(JavaScript 的調(diào)用棧)無(wú)法一次抓住所有的拼圖塊,導(dǎo)致它們?yōu)⒙湟坏兀R绯鲥e(cuò)誤)。
那么,如何優(yōu)雅地解決這個(gè)問(wèn)題,實(shí)現(xiàn)大型 ArrayBuffer 到 Base64 的轉(zhuǎn)換呢?讓我們探索幾種有效的方法。
解決方案詳解
使用
reduce
? 方法這種方法就像用一個(gè)小勺子,一勺一勺地舀水。雖然不會(huì)溢出,但可能會(huì)花很長(zhǎng)時(shí)間。
const base64 = btoa(new Uint8Array(arrayBuffer).reduce((data, byte) => data + String.fromCharCode(byte), ''));
工作原理:
- 首先,將 ArrayBuffer 轉(zhuǎn)換為 Uint8Array,就像把水倒入一個(gè)個(gè)小杯子里。
- 然后,使用
reduce
? 方法遍歷每個(gè)字節(jié)(每個(gè)小杯子),將其轉(zhuǎn)換為字符。 - 每次迭代都會(huì)創(chuàng)建一個(gè)新的字符串,就像把每個(gè)小杯子的水倒入一個(gè)逐漸變大的容器中。
- 最后,使用
btoa
? 將得到的字符串轉(zhuǎn)換為 Base64。
為什么慢:
- 字符串拼接操作(
data + String.fromCharCode(byte)
?)在每次迭代中都會(huì)創(chuàng)建一個(gè)新的字符串。 - 對(duì)于大型 ArrayBuffer,這意味著創(chuàng)建成千上萬(wàn)個(gè)中間字符串,就像在倒水過(guò)程中不斷更換容器。
- 這種頻繁的內(nèi)存分配和釋放操作會(huì)顯著降低性能。
現(xiàn)代方法:TextDecoder + btoa
這種方法就像擁有一臺(tái)高效的自動(dòng)灌裝機(jī)。它能迅速將整桶水(ArrayBuffer)直接灌入瓶子(Base64字符串),既快速又安全。
const text = new TextDecoder().decode(new Uint8Array(arrayBuffer)); const base64 = btoa(text);
工作原理:
- TextDecoder 像一個(gè)智能轉(zhuǎn)換器,能夠一次性將整個(gè) Uint8Array 轉(zhuǎn)換為字符串。
- 這個(gè)過(guò)程就像是用一根大管子,直接將水從桶中抽出并過(guò)濾。
- 然后,
btoa
? 函數(shù)作為最后的包裝步驟,將字符串轉(zhuǎn)換為 Base64 編碼。
為什么快:
- TextDecoder 是在底層實(shí)現(xiàn)的,利用了瀏覽器的原生優(yōu)化。就像一臺(tái)精心設(shè)計(jì)的工業(yè)級(jí)設(shè)備。
- 它能夠一次性處理整個(gè)數(shù)組,避免了頻繁的字符串創(chuàng)建和拼接操作。
在實(shí)際編程中,對(duì)于小型數(shù)據(jù),兩種方法的差異可能不明顯。但當(dāng)處理大型 ArrayBuffer(比如高分辨率圖片數(shù)據(jù))時(shí),現(xiàn)代方法的優(yōu)勢(shì)就會(huì)非常明顯,可能會(huì)將處理時(shí)間從秒級(jí)降低到毫秒級(jí)。
兼容代碼
const arrayBufferToBase64 = (buffer) => { if (typeof TextDecoder !== 'undefined' && typeof btoa !== 'undefined') { return btoa(new TextDecoder().decode(new Uint8Array(buffer))); } else { return btoa(new Uint8Array(buffer).reduce((data, byte) => data + String.fromCharCode(byte), '')); } }
這個(gè)函數(shù)首先檢查環(huán)境是否支持 TextDecoder 和 btoa。如果支持,就使用高性能的現(xiàn)代方法;如果不支持,則回退到使用 reduce 方法,確保最大兼容性。
結(jié)語(yǔ)
在處理 ArrayBuffer 到 Base64 的轉(zhuǎn)換時(shí),現(xiàn)代的 TextDecoder + btoa 方法通常是最佳選擇,但在需要更廣泛兼容性的情況下,可以考慮使用 reduce 方法作為備選。
以上就是JavaScript實(shí)現(xiàn)ArrayBuffer到Base64的轉(zhuǎn)換的詳細(xì)內(nèi)容,更多關(guān)于JavaScript ArrayBuffer轉(zhuǎn)Base64的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
基于JS如何實(shí)現(xiàn)給字符加千分符(65,541,694,158)
JS如何實(shí)現(xiàn)給字符加千分符,本文給大家?guī)?lái)了基于js實(shí)現(xiàn)的代碼,代碼簡(jiǎn)單易懂,感興趣的朋友一起學(xué)習(xí)吧2016-08-08JS實(shí)現(xiàn)星星評(píng)分功能實(shí)例代碼(兩種方法)
這篇文章主要介紹了JS實(shí)現(xiàn)星星評(píng)分功能實(shí)例代碼(兩種方法)的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-06-06懶就要懶到底——鼠標(biāo)自動(dòng)點(diǎn)擊(含時(shí)間判斷)
懶就要懶到底——鼠標(biāo)自動(dòng)點(diǎn)擊(含時(shí)間判斷)...2007-02-02js獲取元素到可視區(qū)的距離、瀏覽器窗口滾動(dòng)距離及元素距離瀏覽器頂部距離
這篇文章主要給大家介紹了關(guān)于js獲取元素到可視區(qū)的距離、瀏覽器窗口滾動(dòng)距離及元素距離瀏覽器頂部距離的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-11-11深入理解javascript嚴(yán)格模式(Strict Mode)
Strict mode是JavaScript1.8.5引進(jìn)的技術(shù),但還沒(méi)有瀏覽器確實(shí)可靠的實(shí)現(xiàn)了嚴(yán)格模式,所以使用時(shí)要小心并且多測(cè)試。Strict mode可以應(yīng)用于整個(gè)腳本,也可以適合于單個(gè)函數(shù)。2014-11-11