diff --git a/package.json b/package.json index 7b77ca68e5..8a7115abde 100644 --- a/package.json +++ b/package.json @@ -36,8 +36,10 @@ "devDependencies": { "@crowdin/cli": "^3.7.10", "@hrgui/libass-wasm-ts": "^1.0.3", + "@types/crypto-js": "^4.2.2", "@types/mark.js": "^8.11.8", "@types/node": "^20.0.0", + "@types/qrcode": "^1.5.5", "@types/sha256": "^0.2.0", "@types/streamsaver": "^2.0.1", "@vitejs/plugin-legacy": "^2.0.1", @@ -61,7 +63,6 @@ "@solid-primitives/keyboard": "^1.2.5", "@solid-primitives/storage": "^1.3.1", "@stitches/core": "^1.2.8", - "@types/qrcode": "^1.5.5", "@viselect/vanilla": "^3.5.0", "aplayer": "^1.10.1", "artplayer": "^5.0.9", @@ -73,6 +74,7 @@ "copy-to-clipboard": "^3.3.2", "crypto-js": "^4.2.0", "flv.js": "^1.6.2", + "hash-wasm": "^4.12.0", "hls.js": "^1.2.1", "just-once": "^2.2.0", "libass-wasm": "^4.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d7347e6c8e..eaef3bb0b9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,9 +35,6 @@ importers: '@stitches/core': specifier: ^1.2.8 version: 1.2.8 - '@types/qrcode': - specifier: ^1.5.5 - version: 1.5.5 '@viselect/vanilla': specifier: ^3.5.0 version: 3.5.0 @@ -71,6 +68,9 @@ importers: flv.js: specifier: ^1.6.2 version: 1.6.2 + hash-wasm: + specifier: ^4.12.0 + version: 4.12.0 hls.js: specifier: ^1.2.1 version: 1.2.1 @@ -138,12 +138,18 @@ importers: '@hrgui/libass-wasm-ts': specifier: ^1.0.3 version: 1.0.3 + '@types/crypto-js': + specifier: ^4.2.2 + version: 4.2.2 '@types/mark.js': specifier: ^8.11.8 version: 8.11.8 '@types/node': specifier: ^20.0.0 version: 20.0.0 + '@types/qrcode': + specifier: ^1.5.5 + version: 1.5.5 '@types/sha256': specifier: ^0.2.0 version: 0.2.0 @@ -486,6 +492,9 @@ packages: '@stitches/core@1.2.8': resolution: {integrity: sha512-Gfkvwk9o9kE9r9XNBmJRfV8zONvXThnm1tcuojL04Uy5uRyqg93DC83lDebl0rocZCfKSjUv+fWYtMQmEDJldg==} + '@types/crypto-js@4.2.2': + resolution: {integrity: sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==} + '@types/debug@4.1.7': resolution: {integrity: sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==} @@ -1091,6 +1100,9 @@ packages: resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} engines: {node: '>= 0.4.0'} + hash-wasm@4.12.0: + resolution: {integrity: sha512-+/2B2rYLb48I/evdOIhP+K/DD2ca2fgBjp6O+GBEnCDk2e4rpeXIK8GvIyRPjTezgmWn9gmKwkQjjx6BtqDHVQ==} + hast-to-hyperscript@10.0.1: resolution: {integrity: sha512-dhIVGoKCQVewFi+vz3Vt567E4ejMppS1haBRL6TEmeLeJVB1i/FJIIg/e6s1Bwn0g5qtYojHEKvyGA+OZuyifw==} @@ -2419,6 +2431,8 @@ snapshots: '@stitches/core@1.2.8': {} + '@types/crypto-js@4.2.2': {} + '@types/debug@4.1.7': dependencies: '@types/ms': 0.7.31 @@ -2956,6 +2970,8 @@ snapshots: dependencies: function-bind: 1.1.1 + hash-wasm@4.12.0: {} + hast-to-hyperscript@10.0.1: dependencies: '@types/unist': 2.0.6 diff --git a/src/lang/en/home.json b/src/lang/en/home.json index 643cad374f..7178f2aa2f 100644 --- a/src/lang/en/home.json +++ b/src/lang/en/home.json @@ -93,6 +93,7 @@ }, "upload": { "add_as_task": "Add as task", + "try_rapid": "Try rapid", "upload-tips": "Drag files here to upload, or click:", "release": "Release to upload", "no_files_drag": "No files were dragged in.", diff --git a/src/pages/home/uploads/Upload.tsx b/src/pages/home/uploads/Upload.tsx index 51c4bf850a..72ef1cefaa 100644 --- a/src/pages/home/uploads/Upload.tsx +++ b/src/pages/home/uploads/Upload.tsx @@ -77,6 +77,7 @@ const Upload = () => { const [uploading, setUploading] = createSignal(false) const [asTask, setAsTask] = createSignal(false) const [overwrite, setOverwrite] = createSignal(false) + const [rapid, setRapid] = createSignal(true) const [uploadFiles, setUploadFiles] = createStore<{ uploads: UploadFileProps[] }>({ @@ -119,6 +120,7 @@ const Upload = () => { }, asTask(), overwrite(), + rapid(), ) if (!err) { setUpload(path, "status", "success") @@ -293,6 +295,14 @@ const Upload = () => { > {t("home.overwrite_existing")} + { + setRapid(!rapid()) + }} + > + {t("home.upload.try_rapid")} + diff --git a/src/pages/home/uploads/form.ts b/src/pages/home/uploads/form.ts index db11caaae9..85d90fe2f6 100644 --- a/src/pages/home/uploads/form.ts +++ b/src/pages/home/uploads/form.ts @@ -2,26 +2,35 @@ import { password } from "~/store" import { EmptyResp } from "~/types" import { r } from "~/utils" import { SetUpload, Upload } from "./types" +import { calculateHash } from "./util" export const FormUpload: Upload = async ( uploadPath: string, file: File, setUpload: SetUpload, asTask = false, overwrite = false, + rapid = false, ): Promise => { let oldTimestamp = new Date().valueOf() let oldLoaded = 0 const form = new FormData() form.append("file", file) + let headers: { [k: string]: any } = { + "File-Path": encodeURIComponent(uploadPath), + "As-Task": asTask, + "Content-Type": "multipart/form-data", + "Last-Modified": file.lastModified, + Password: password(), + Overwrite: overwrite.toString(), + } + if (rapid) { + const { md5, sha1, sha256 } = await calculateHash(file) + headers["X-File-Md5"] = md5 + headers["X-File-Sha1"] = sha1 + headers["X-File-Sha256"] = sha256 + } const resp: EmptyResp = await r.put("/fs/form", form, { - headers: { - "File-Path": encodeURIComponent(uploadPath), - "As-Task": asTask, - "Content-Type": "multipart/form-data", - "Last-Modified": file.lastModified, - Password: password(), - Overwrite: overwrite.toString(), - }, + headers: headers, onUploadProgress: (progressEvent) => { if (progressEvent.total) { const complete = diff --git a/src/pages/home/uploads/stream.ts b/src/pages/home/uploads/stream.ts index 1c725a716b..72980cc182 100644 --- a/src/pages/home/uploads/stream.ts +++ b/src/pages/home/uploads/stream.ts @@ -2,24 +2,33 @@ import { password } from "~/store" import { EmptyResp } from "~/types" import { r } from "~/utils" import { SetUpload, Upload } from "./types" +import { calculateHash } from "./util" export const StreamUpload: Upload = async ( uploadPath: string, file: File, setUpload: SetUpload, asTask = false, overwrite = false, + rapid = false, ): Promise => { let oldTimestamp = new Date().valueOf() let oldLoaded = 0 + let headers: { [k: string]: any } = { + "File-Path": encodeURIComponent(uploadPath), + "As-Task": asTask, + "Content-Type": file.type || "application/octet-stream", + "Last-Modified": file.lastModified, + Password: password(), + Overwrite: overwrite.toString(), + } + if (rapid) { + const { md5, sha1, sha256 } = await calculateHash(file) + headers["X-File-Md5"] = md5 + headers["X-File-Sha1"] = sha1 + headers["X-File-Sha256"] = sha256 + } const resp: EmptyResp = await r.put("/fs/put", file, { - headers: { - "File-Path": encodeURIComponent(uploadPath), - "As-Task": asTask, - "Content-Type": file.type || "application/octet-stream", - "Last-Modified": file.lastModified, - Password: password(), - Overwrite: overwrite.toString(), - }, + headers: headers, onUploadProgress: (progressEvent) => { if (progressEvent.total) { const complete = diff --git a/src/pages/home/uploads/types.ts b/src/pages/home/uploads/types.ts index c138d59455..ade472f71e 100644 --- a/src/pages/home/uploads/types.ts +++ b/src/pages/home/uploads/types.ts @@ -22,4 +22,5 @@ export type Upload = ( setUpload: SetUpload, asTask: boolean, overwrite: boolean, + rapid: boolean, ) => Promise diff --git a/src/pages/home/uploads/util.ts b/src/pages/home/uploads/util.ts index a3ac26a393..ee7fa88cc4 100644 --- a/src/pages/home/uploads/util.ts +++ b/src/pages/home/uploads/util.ts @@ -1,4 +1,5 @@ import { UploadFileProps } from "./types" +import { createMD5, createSHA1, createSHA256 } from "hash-wasm" export const traverseFileTree = async (entry: FileSystemEntry) => { let res: File[] = [] @@ -59,3 +60,25 @@ export const File2Upload = (file: File): UploadFileProps => { status: "pending", } } + +export const calculateHash = async (file: File) => { + const md5Digest = await createMD5() + const sha1Digest = await createSHA1() + const sha256Digest = await createSHA256() + const reader = file.stream().getReader() + const read = async () => { + const { done, value } = await reader.read() + if (done) { + return + } + md5Digest.update(value) + sha1Digest.update(value) + sha256Digest.update(value) + await read() + } + await read() + const md5 = md5Digest.digest("hex") + const sha1 = sha1Digest.digest("hex") + const sha256 = sha256Digest.digest("hex") + return { md5, sha1, sha256 } +}