如何在 img 標籤上顯示從 fetch 得到的圖片

這篇文章來起因於今日課堂上心血來潮,突然想範例一個前端取得 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 改為同步呼叫。

發表迴響