From ae2fbcc216c9995bad40c06ae3f766d9bcbb12d0 Mon Sep 17 00:00:00 2001 From: keven1024 Date: Wed, 8 Apr 2026 23:45:34 +0800 Subject: [PATCH] feat(front): enhance file hash calculation by introducing engine selection for large files using native or wasm methods --- .../File/FileUploadProgressView/index.vue | 5 +- front/lib/calcFileHash.ts | 53 +++++++------------ front/lib/calcFileHashWorker.ts | 6 +-- 3 files changed, 27 insertions(+), 37 deletions(-) diff --git a/front/components/Home/File/FileUploadProgressView/index.vue b/front/components/Home/File/FileUploadProgressView/index.vue index 4a5b254..a0dc096 100644 --- a/front/components/Home/File/FileUploadProgressView/index.vue +++ b/front/components/Home/File/FileUploadProgressView/index.vue @@ -137,11 +137,14 @@ watchEffect(async () => { } }) +const LARGE_FILE_THRESHOLD = 500 * 1024 * 1024 // 500 MB + const handleHash = async (fileId: string) => { const uploadfile = uploadfiles.value.find((item) => item.fileId === fileId) if (!uploadfile?.file) return uploadfile.procressType = 'hash' - const res = await asyncWorker(calcFileHashWorker, { data: { file: uploadfile.file } }) + const engine = uploadfile.file.size >= LARGE_FILE_THRESHOLD ? 'wasm' : 'native' + const res = await asyncWorker(calcFileHashWorker, { data: { file: uploadfile.file, engine } }) const { hash } = res?.data || {} uploadfile.hash = hash } diff --git a/front/lib/calcFileHash.ts b/front/lib/calcFileHash.ts index 152e0c4..f7894c3 100644 --- a/front/lib/calcFileHash.ts +++ b/front/lib/calcFileHash.ts @@ -1,47 +1,34 @@ import { noop } from 'lodash-es' -import { md5 } from 'js-md5' +import { createSHA1 } from 'hash-wasm' interface CalcFileHashProps { file: File onProgress?: (current: number) => void chunkSize?: number + engine?: 'native' | 'wasm' } const calcFileHash = async (props: CalcFileHashProps) => { - const { file, onProgress = noop, chunkSize = 100 } = props || {} - const blob = await file.arrayBuffer() - const hash = md5(blob) - return hash - // const finalChunkSize = chunkSize * 1024 * 1024; - // const chunks = Math.ceil(file.size / finalChunkSize); - // const spark = new SparkMD5.ArrayBuffer(); // 使用 SparkMD5 增量计算哈希 - // const fileReader = new FileReader(); + const { file, onProgress = noop, chunkSize = 100, engine = 'native' } = props || {} - // const readChunk = (start: number): Promise => { - // return new Promise((resolve, reject) => { - // const chunk = file.slice(start, Math.min(start + finalChunkSize, file.size)); - // fileReader.onload = (e) => resolve(e.target?.result as ArrayBuffer); - // fileReader.onerror = reject; - // fileReader.readAsArrayBuffer(chunk); - // }); - // }; + if (engine === 'native') { + const buffer = await file.arrayBuffer() + const hashBuffer = await crypto.subtle.digest('SHA-1', buffer) + return Array.from(new Uint8Array(hashBuffer)) + .map((b) => b.toString(16).padStart(2, '0')) + .join('') + } - // try { - // const progressCallback = (current: number) => { - // const percentage = Math.round((current / chunks) * 100); - // onProgress(percentage); - // }; - - // for (let i = 0; i < chunks; i++) { - // const chunk = await readChunk(i * chunkSize); - // spark.append(chunk); - // progressCallback(i + 1); - // } - - // return spark.end(); - // } catch (error) { - // throw error; - // } + const chunkBytes = chunkSize * 1024 * 1024 + const hasher = await createSHA1() + let offset = 0 + while (offset < file.size) { + const buffer = await file.slice(offset, offset + chunkBytes).arrayBuffer() + hasher.update(new Uint8Array(buffer)) + offset += chunkBytes + onProgress(Math.min(offset, file.size) / file.size) + } + return hasher.digest('hex') } export default calcFileHash diff --git a/front/lib/calcFileHashWorker.ts b/front/lib/calcFileHashWorker.ts index 423722d..5306810 100644 --- a/front/lib/calcFileHashWorker.ts +++ b/front/lib/calcFileHashWorker.ts @@ -1,8 +1,8 @@ import calcFileHash from './calcFileHash' // 监听主线程消息 -self.onmessage = async (e: MessageEvent<{ file: File }>) => { - const { file } = e.data || {} - const hash = await calcFileHash({ file }) +self.onmessage = async (e: MessageEvent<{ file: File; engine?: 'native' | 'wasm' }>) => { + const { file, engine } = e.data || {} + const hash = await calcFileHash({ file, engine }) self.postMessage({ hash }) }