上傳檔案時,API 會要求先計算出檔案的 MD5 連同檔案一起上傳,後端再根據 MD5 確認檔案是否傳輸正確,JavaScript 該如何計算出檔案的 MD5 呢 ?
Version
Vue 3.0.11
SparkMD5 3.0.1
Add JavaScript MD5
$ yarn add spark-md5
使用 Yarn 安裝 JavaScript MD5。
顯示 String 的 MD5。
<template lang='pub'>
div {{ hash }}
</template>
<script setup>
import SparkMD5 from 'spark-md5'
let spark = new SparkMD5
spark.append('Hello World')
let hash = spark.end()
</script>
第 6 行
import SparkMD5 from 'spark-md5'
從 spark-md5
引用 SparkMD5
class。
第 8 行
let spark = new SparkMD5
spark.append('Hello World')
let hash = spark.end()
- 將 String 傳入
spark.append()
- 以
spark.end()
讀取 MD5
這是最簡單的將 String 轉成 MD5,可先確認 JavaScript MD5 環境是否設定成功
FileReader
上傳檔案計算出檔案的 MD5。
<template lang='pug'>
input(@input='onInput', ref='fileUpload', type='file', accept='images/*', multiple, hidden)
button(@click='onUpload').bg-blue-500.hover_bg-blue-700.text-white.font-bold.py-2.px-4.rounded Upload
span {{ hash }}
</template>
<script setup>
import SparkMD5 from 'spark-md5'
ref: hash = ''
ref: fileUpload = null
let onUpload = () => fileUpload.click()
let onInput = () => {
let fileReader = new FileReader
fileReader.onload = () => {
let spark = new SparkMD5
spark.appendBinary(fileReader.result)
hash = spark.end()
}
fileReader.onerror = console.error
fileReader.readAsBinaryString(fileUpload.files[0])
}
</script>
第 2 行
input(@input='onInput', ref='fileUpload', type='file', accept='images/*', multiple, hidden)
button(@click='onUpload').bg-blue-500.hover_bg-blue-700.text-white.font-bold.py-2.px-4.rounded Upload
使用 Tailwind CSS 實現 File Upload。
13 行
let onUpload = () => fileUpload.click()
當 button click 時,執行 fileUpload.click()
。
15 行
let onInput = () => {}
當 fileUpload.click()
執行時是會執行 onInput()
。
17 行
let fileReader = new FileReader
fileReader.onload = () => {
let spark = new SparkMD5
spark.appendBinary(fileReader.result)
hash = spark.end()
}
fileReader.onerror = console.error
JavaScript MD5 雖然可以計算出 MD5,但首先必須使用
FileReader
將檔案轉成 Binary String 之後再透過 JavaScript MD5 計算不過
FileReader
以 event-based API 實現 asynchronous,必須在onload
event 內讀取fileReader.result
才能得到 Binary String
24 行
fileReader.readAsBinaryString(fileUpload.files[0])
將所選擇檔案傳入 fileReader.readAsBinaryString()
即可得到 MD5。
Promise
結果不變,但使用 Promise 改寫。
<template lang='pug'>
input(@input='onInput', ref='fileUpload', type='file', accept='images/*', multiple, hidden)
button(@click='onUpload').bg-blue-500.hover_bg-blue-700.text-white.font-bold.py-2.px-4.rounded Upload
span {{ hash }}
</template>
<script setup>
import SparkMD5 from 'spark-md5'
ref: hash = ''
ref: fileUpload = null
let toMD5 = file => new Promise((resolve, reject) => {
let fileReader = new FileReader
fileReader.onload = () => {
let spark = new SparkMD5
spark.appendBinary(fileReader.result)
resolve(spark.end())
}
fileReader.onerror = reject
fileReader.readAsBinaryString(file)
})
let onUpload = () => fileUpload.click()
let onInput = () =>
toMD5(fileUpload.files[0])
.then(x => hash = x)
.catch(console.error)
</script>
FileReader
雖然可用,但畢竟屬於較早期 Web API,asynchronous 仍使用 event 處理,比較好的方式是改用 Promise。
13 行
let toMD5 = file => new Promise((resolve, reject) => {
let fileReader = new FileReader
fileReader.onload = () => {
let spark = new SparkMD5
spark.appendBinary(fileReader.result)
resolve(spark.end())
}
fileReader.onerror = reject
fileReader.readAsBinaryString(file)
})
自行建立 toMD5()
,使用 new Promise((resolve, reject) => {})
建立 Promise,在 onload
使用 resolve()
回傳 MD5,且在 onerror
使用 reject()
回傳 error。
最後將 file
傳入 fileReader.readAsBinaryString()
轉成 Binary String。
26 行
let onInput = () =>
toMD5(fileUpload.files[0])
.then(x => hash = x)
.catch(console.error)
toMD5()
回傳 Promise 後,就可使用 Promise Chain 處理 MD5。
Point-free
結果不變,但使用 Point-free 改寫。
<template lang='pug'>
input(@input='onInput', ref='fileUpload', type='file', accept='images/*', multiple, hidden)
button(@click='onUpload').bg-blue-500.hover_bg-blue-700.text-white.font-bold.py-2.px-4.rounded Upload
span {{ hash }}
</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'
import SparkMD5 from 'spark-md5'
let hash = ref('')
let fileUpload = ref(null)
let toMD5 = file => new Promise((resolve, reject) => {
let fileReader = new FileReader
fileReader.onload = () => {
let spark = new SparkMD5
spark.appendBinary(fileReader.result)
resolve(spark.end())
}
fileReader.onerror = reject
fileReader.readAsBinaryString(file)
})
let onUpload = () => unref(fileUpload).click()
let onInput = pipe(
read(fileUpload),
prop('files'),
head,
toMD5,
then(write(hash)),
otherwise(error)
)
</script>
30 行
let onInput = pipe(
read(fileUpload),
prop('files'),
head,
toMD5,
then(write(hash)),
otherwise(error)
)
使用 pipe()
組合出 onInput()
:
read(fileUpload)
:讀取fileUpload
refprop('files')
:讀取files
prophead
:files
為 Array,取其第一筆toMD5
:將 file 轉成 MD5then(write(hash))
:toMD5()
回傳為 Promise,使用then()
拆解後寫入hash
stateotherwise(error)
:處理 Rejected Promise
Conclusion
- 實務上較少使用
new Promise((resolve, reject) => {})
建立 Promise,但遇到FileReader
這類 event-based API,只能使用這種方式回傳 Promise - JavaScript 預設無法計算出 MD5,必須另外安裝 Package;若要計算檔案的 MD5,先要透過內建
FilerReader
將檔案轉成 Binary String 後,再計算其 MD5