使用 File Upload 選擇圖片後,接著會將檔案轉成 Base64 String 透過 API 上傳,該如何將檔案轉成 Base64 String 呢 ?
Version
Vue 3.2
FileReader
上傳檔案計算出檔案的 Base64 並顯示。
<template>
<input type="file" @input="onInput" ref="fileUpload" accept="images/*" multiple hidden>
<button class="bg-blue-500 hover_bg-blue-700 text-white font-bold py-2 px-4 rounded" @click="onUpload">Upload</button>
<img :src="base64">
</template>
<script setup>
let base64 = $ref ('')
let fileUpload = $ref (null)
let onUpload = _ => fileUpload.click ()
let onInput = _ => {
let fileReader = new FileReader
fileReader.onload = _ => base64 = fileReader.result
fileReader.onerror = console.error
fileReader.readAsDataURL (fileUpload.files[0])
}
</script>
14 行
let fileReader = new FileReader
fileReader.onload = _ => base64 = fileReader.result
fileReader.onerror = console.error
要將檔案計算出 Base64 不難,HTML 內建的 FileReader
就可完成。
不過 FileReader
以 event-based API 實現 asynchronous,必須在 onload
event 內讀取 fileReader.result
才能得到 Base64。
17 行
fileReader.readAsDataURL (fileUpload.files[0])
將 file 傳入 fileReader.readAsDataURL
即可得到 Base64。
Promise
結果不變,但使用 Promise 改寫。
<template>
<input type="file" @input="onInput" ref="fileUpload" accept="images/*" multiple hidden>
<button class="bg-blue-500 hover_bg-blue-700 text-white font-bold py-2 px-4 rounded" @click="onUpload">Upload</button>
<img :src="base64">
</template>
<script setup>
let base64 = $ref ('')
let fileUpload = $ref (null)
let toBase64 = file => new Promise ((resolve, reject) => {
let fileReader = new FileReader
fileReader.onload = _ => resolve (fileReader.result)
fileReader.onerror = reject
fileReader.readAsDataURL (file)
})
let onUpload = _ => fileUpload.click ()
let onInput = _ =>
toBase64 (fileUpload.files[0])
.then (x => base64 = x)
.catch (console.error)
</script>
FileReader
雖然可用,但畢竟屬於較早期 Web API,asynchronous 仍使用 event 處理,比較好的方式是改用 Promise。
11 行
let toBase64 = file => new Promise ((resolve, reject) => {
let fileReader = new FileReader
fileReader.onload = _ => resolve (fileReader.result)
fileReader.onerror = reject
fileReader.readAsDataURL (file)
})
自行建立 toBase64
,使用 new Promise ((resolve, reject) => {})
建立 Promise,在 onload
使用 resolve
回傳 fileReader.result
,且在 onerror
使用 reject
回傳 error。
將 file
傳入 fileReader.readAsDataURL
計算出 Base64。
21 行
toBase64 (fileUpload.files[0])
.then (x => base64 = x)
.catch (console.error)
toBase64
回傳 Promise 後,就可使用 Promise Chain 處理 Base64。
Point-free
結果不變,但使用 Point-free 改寫。
<template>
<input type="file" @input="onInput" ref="fileUpload" accept="images/*" multiple hidden>
<button class="bg-blue-500 hover_bg-blue-700 text-white font-bold py-2 px-4 rounded" @click="onUpload">Upload</button>
<img :src="base64">
</template>
<script setup>
import { ref, unref } from 'vue'
import { read, write } from 'vue3-fp'
import { pipe, prop, andThen as then, otherwise, head } from 'ramda'
import { error } from 'wink-fp'
let base64 = ref ('')
let fileUpload = ref (null)
let toBase64 = file => new Promise ((resolve, reject) => {
let fileReader = new FileReader
fileReader.onload = _ => resolve (fileReader.result)
fileReader.onerror = reject
fileReader.readAsDataURL (file)
})
let onUpload = _ => unref (fileUpload).click ()
let onInput = pipe (
read (fileUpload),
prop ('files'),
head,
toBase64,
then (write (base64)),
otherwise (error)
)
</script>
25 行
let onInput = pipe (
read (fileUpload),
prop ('files'),
head,
toBase64,
then (write (base64)),
otherwise (error)
)
使用 pipe
組合出 onInput
:
read (fileUpload)
:讀取fileUpload
prop ('files')
:讀取files
prophead
:files
為 Array,取其第一筆toBase64
:將 file 轉成 Base64then (write (base64))
:toBase64
回傳為 Promise,使用then
拆解後寫入base64
stateotherwise (error)
:處理 Rejected
Conclusion
- 實務上較少使用
new Promise ((resolve, reject) => {})
建立 Promise,但遇到FileReader
這類 event-based API,只能使用這種方式回傳 Promise