這篇文章來起因於今日課堂上心血來潮,突然想範例一個前端取得 fetch 回來的 jpeg 原始資料轉成 base64 編碼後顯示到 <img> 標籤上的作法。其實想要在 img 標籤上顯示圖片,常見的作法是取得 blob 資料後透過 URL 類別的 createObjectURL() 函數存檔並且得到一個 URL,然後將此 URL 交給 <img> 標籤的 src 就可以顯示圖片了。先看看常見的作法,很簡單(參考 mozilla 的官方範例)。
網頁內容,如下。
<body>
<button id="bn">click</button><p></p>
<img id="image" style="width: 300px;">
</body>
常見作法,如下。
<script>
window.onload = () => {
bn.onclick = () => {
fetch('elephant.jpg') // 此為要載入的圖片檔名或網址
.then(response => {
return response.blob()
})
.then(blob => {
image.src = URL.createObjectURL(blob)
})
}
}
</script>
接下來看取得 jpeg 圖檔的 raw data 後轉 base64 再顯示到 <img> 標籤上的作法。
<script>
window.onload = () => {
bn.onclick = () => {
fetch('elephant.jpg')
.then(response => {
return response.blob()
})
.then(async blob => {
const byteArray = new Uint8Array(await blob.arrayBuffer()) // [225, 216, 225, ...]
const base64 = btoa(String.fromCharCode(...byteArray)) // byteArray to base64
image.src = `data:${blob.type};base64,${base64}` // blob.type is "image/jpeg"
})
}
}
明顯麻煩很多,但如果需要得到 jpeg 原始資料時,就需要使用這種作法了。要留意 arrayBuffer() 函數傳回的是一個 Promise 物件,雖然可以用 return 的方式將 arrayBuffer() 的結果再丟到下一個 then() 去處理,但因為 <img> 要顯示 base64 編碼的圖片需要除了 base64 字串外還需要 MIME type(別忘了瀏覽器能顯示的圖片格式不是只有 jpeg,見上方最後一行 image.src 右方的字串格式),所以為了同時取得這兩份資料,因此在第二個 then() 裡面處理是最適合的位置,只是要將 arrayBuffer() 透過 await 改為同步呼叫。