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)文章
微信小程序語(yǔ)音同步智能識(shí)別的實(shí)現(xiàn)案例代碼解析
在一些小程序的開(kāi)發(fā)場(chǎng)景中經(jīng)常會(huì)有語(yǔ)音轉(zhuǎn)文字的需求,今天小編通過(guò)實(shí)際案例給大家分享微信小程序語(yǔ)音同步智能識(shí)別功能,需要的朋友可以參考下2020-05-05
JavaScript實(shí)現(xiàn)多叉樹(shù)的遞歸遍歷和非遞歸遍歷算法操作示例
這篇文章主要介紹了JavaScript實(shí)現(xiàn)多叉樹(shù)的遞歸遍歷和非遞歸遍歷算法,結(jié)合實(shí)例形式詳細(xì)分析了JavaScript多叉樹(shù)針對(duì)json節(jié)點(diǎn)的遞歸與非遞歸遍歷相關(guān)操作技巧,需要的朋友可以參考下2018-02-02
使用ngrok+express解決本地環(huán)境中微信接口調(diào)試問(wèn)題
這篇文章主要介紹了使用ngrok+express解決本地環(huán)境中微信接口調(diào)試問(wèn)題,需要的朋友可以參考下2018-02-02
js實(shí)現(xiàn)移動(dòng)端輪播圖滑動(dòng)切換
這篇文章主要為大家詳細(xì)介紹了js實(shí)現(xiàn)移動(dòng)端輪播圖滑動(dòng)切換,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-12-12
JavaScript實(shí)現(xiàn)圖片無(wú)縫滾動(dòng)效果
這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)圖片無(wú)縫滾動(dòng)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07
ES6知識(shí)點(diǎn)整理之String字符串新增常用方法示例
這篇文章主要介紹了ES6知識(shí)點(diǎn)整理之String字符串新增常用方法,結(jié)合實(shí)例形式分析了ES6字符串String includes,startsWith,endsWith等方法相關(guān)使用技巧,需要的朋友可以參考下2019-07-07
JavaScript頁(yè)面模版化配置實(shí)戰(zhàn)詳解
JavaScript模塊化是一種將代碼分割成獨(dú)立、可復(fù)用的單元(模塊)的方法,旨在提高代碼的可維護(hù)性、可重用性和可擴(kuò)展性,這篇文章主要介紹了JavaScript頁(yè)面模版化配置的相關(guān)資料,需要的朋友可以參考下2025-08-08

