From 43173b6bf65b64dd977937838683282895d0a15a Mon Sep 17 00:00:00 2001 From: Yang MingCheng <1107238486@qq.com> Date: Wed, 4 Feb 2026 16:53:43 +0800 Subject: [PATCH 1/2] fix(uploads): resolve hash calculation memory crash and add hash progress --- src/lang/en/home.json | 1 + src/pages/home/uploads/form.ts | 6 +++++- src/pages/home/uploads/stream.ts | 6 +++++- src/pages/home/uploads/types.ts | 9 ++++++++- src/pages/home/uploads/util.ts | 19 ++++++++++++++----- 5 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/lang/en/home.json b/src/lang/en/home.json index c98dfaeb7..dffc08fc1 100644 --- a/src/lang/en/home.json +++ b/src/lang/en/home.json @@ -112,6 +112,7 @@ "no_files_drag": "No files were dragged in.", "upload_files": "Choose Files", "upload_folder": "Choose Folder", + "hashing": "Hashing", "pending": "Pending", "uploading": "Uploading", "backending": "Uploading in the backend", diff --git a/src/pages/home/uploads/form.ts b/src/pages/home/uploads/form.ts index 4800ccf53..5b83af53c 100644 --- a/src/pages/home/uploads/form.ts +++ b/src/pages/home/uploads/form.ts @@ -24,11 +24,15 @@ export const FormUpload: Upload = async ( Overwrite: overwrite.toString(), } if (rapid) { - const { md5, sha1, sha256 } = await calculateHash(file) + setUpload("status", "hashing") + const { md5, sha1, sha256 } = await calculateHash(file, (p) => { + setUpload("progress", p | 0) + }) headers["X-File-Md5"] = md5 headers["X-File-Sha1"] = sha1 headers["X-File-Sha256"] = sha256 } + setUpload("status", "uploading") const resp: EmptyResp = await r.put("/fs/form", form, { headers: headers, onUploadProgress: (progressEvent) => { diff --git a/src/pages/home/uploads/stream.ts b/src/pages/home/uploads/stream.ts index 2f9e260c5..62c3793b7 100644 --- a/src/pages/home/uploads/stream.ts +++ b/src/pages/home/uploads/stream.ts @@ -22,11 +22,15 @@ export const StreamUpload: Upload = async ( Overwrite: overwrite.toString(), } if (rapid) { - const { md5, sha1, sha256 } = await calculateHash(file) + setUpload("status", "hashing") + const { md5, sha1, sha256 } = await calculateHash(file, (p) => { + setUpload("progress", p | 0) + }) headers["X-File-Md5"] = md5 headers["X-File-Sha1"] = sha1 headers["X-File-Sha256"] = sha256 } + setUpload("status", "uploading") const resp: EmptyResp = await r.put("/fs/put", file, { headers: headers, onUploadProgress: (progressEvent) => { diff --git a/src/pages/home/uploads/types.ts b/src/pages/home/uploads/types.ts index ade472f71..bd366d5db 100644 --- a/src/pages/home/uploads/types.ts +++ b/src/pages/home/uploads/types.ts @@ -1,4 +1,10 @@ -type Status = "pending" | "uploading" | "backending" | "success" | "error" +type Status = + | "pending" + | "hashing" + | "uploading" + | "backending" + | "success" + | "error" export interface UploadFileProps { name: string path: string @@ -10,6 +16,7 @@ export interface UploadFileProps { } export const StatusBadge = { pending: "neutral", + hashing: "warning", uploading: "info", backending: "info", success: "success", diff --git a/src/pages/home/uploads/util.ts b/src/pages/home/uploads/util.ts index aacd6a8dc..966a5f237 100644 --- a/src/pages/home/uploads/util.ts +++ b/src/pages/home/uploads/util.ts @@ -68,22 +68,31 @@ export const File2Upload = (file: File): UploadFileProps => { } } -export const calculateHash = async (file: File) => { +export const calculateHash = async ( + file: File, + onProgress?: (progress: number) => void, +) => { const md5Digest = await createMD5() const sha1Digest = await createSHA1() const sha256Digest = await createSHA256() const reader = file.stream().getReader() - const read = async () => { + let count = 0 + let loaded = 0 + while (true) { const { done, value } = await reader.read() if (done) { - return + break } + loaded += value.length md5Digest.update(value) sha1Digest.update(value) sha256Digest.update(value) - await read() + onProgress?.((loaded / file.size) * 100) + count++ + if (count % 10 === 0) { + await new Promise((resolve) => setTimeout(resolve, 0)) + } } - await read() const md5 = md5Digest.digest("hex") const sha1 = sha1Digest.digest("hex") const sha256 = sha256Digest.digest("hex") From 38b3c5b18ce03d719e2ad13c3464753916914639 Mon Sep 17 00:00:00 2001 From: Yang MingCheng <1107238486@qq.com> Date: Mon, 9 Feb 2026 12:08:07 +0800 Subject: [PATCH 2/2] =?UTF-8?q?refactor(uploads):=20=E4=BD=BF=E7=94=A8=20W?= =?UTF-8?q?eb=20Worker=20=E5=A4=84=E7=90=86=E5=93=88=E5=B8=8C=E8=AE=A1?= =?UTF-8?q?=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/home/uploads/hash-worker.ts | 38 ++++++++++++++++++++ src/pages/home/uploads/util.ts | 50 +++++++++++++-------------- 2 files changed, 62 insertions(+), 26 deletions(-) create mode 100644 src/pages/home/uploads/hash-worker.ts diff --git a/src/pages/home/uploads/hash-worker.ts b/src/pages/home/uploads/hash-worker.ts new file mode 100644 index 000000000..0172cd68d --- /dev/null +++ b/src/pages/home/uploads/hash-worker.ts @@ -0,0 +1,38 @@ +import { createMD5, createSHA1, createSHA256 } from "hash-wasm" + +self.onmessage = async (e: MessageEvent<{ file: File }>) => { + const { file } = e.data + try { + const md5Digest = await createMD5() + const sha1Digest = await createSHA1() + const sha256Digest = await createSHA256() + const reader = file.stream().getReader() + let loaded = 0 + while (true) { + const { done, value } = await reader.read() + if (done) { + break + } + loaded += value.length + md5Digest.update(value) + sha1Digest.update(value) + sha256Digest.update(value) + self.postMessage({ + type: "progress", + progress: (loaded / file.size) * 100, + }) + } + const md5 = md5Digest.digest("hex") + const sha1 = sha1Digest.digest("hex") + const sha256 = sha256Digest.digest("hex") + self.postMessage({ + type: "result", + hash: { md5, sha1, sha256 }, + }) + } catch (error) { + self.postMessage({ + type: "error", + error: error instanceof Error ? error.message : String(error), + }) + } +} diff --git a/src/pages/home/uploads/util.ts b/src/pages/home/uploads/util.ts index 966a5f237..143caa848 100644 --- a/src/pages/home/uploads/util.ts +++ b/src/pages/home/uploads/util.ts @@ -1,5 +1,4 @@ import { UploadFileProps } from "./types" -import { createMD5, createSHA1, createSHA256 } from "hash-wasm" export const traverseFileTree = async (entry: FileSystemEntry) => { let res: File[] = [] @@ -72,29 +71,28 @@ export const calculateHash = async ( file: File, onProgress?: (progress: number) => void, ) => { - const md5Digest = await createMD5() - const sha1Digest = await createSHA1() - const sha256Digest = await createSHA256() - const reader = file.stream().getReader() - let count = 0 - let loaded = 0 - while (true) { - const { done, value } = await reader.read() - if (done) { - break - } - loaded += value.length - md5Digest.update(value) - sha1Digest.update(value) - sha256Digest.update(value) - onProgress?.((loaded / file.size) * 100) - count++ - if (count % 10 === 0) { - await new Promise((resolve) => setTimeout(resolve, 0)) - } - } - const md5 = md5Digest.digest("hex") - const sha1 = sha1Digest.digest("hex") - const sha256 = sha256Digest.digest("hex") - return { md5, sha1, sha256 } + return new Promise<{ md5: string; sha1: string; sha256: string }>( + (resolve, reject) => { + const worker = new Worker(new URL("./hash-worker.ts", import.meta.url), { + type: "module", + }) + worker.postMessage({ file }) + worker.onmessage = (e) => { + const { type, progress, hash, error } = e.data + if (type === "progress") { + onProgress?.(progress) + } else if (type === "result") { + worker.terminate() + resolve(hash) + } else if (type === "error") { + worker.terminate() + reject(new Error(error)) + } + } + worker.onerror = (e) => { + worker.terminate() + reject(e) + } + }, + ) }