From 1f94d0e46524a85a3b8977ad794e26f099218eb4 Mon Sep 17 00:00:00 2001 From: fantasticit Date: Sat, 4 Jun 2022 19:03:39 +0800 Subject: [PATCH] client: use web-woker to calculate md5 --- packages/client/src/services/file.ts | 119 ++++++++++-------- packages/client/src/services/spark-md5.js | 31 +++++ .../tiptap/core/wrappers/attachment/index.tsx | 6 +- packages/client/tsconfig.json | 2 +- 4 files changed, 103 insertions(+), 55 deletions(-) create mode 100644 packages/client/src/services/spark-md5.js diff --git a/packages/client/src/services/file.ts b/packages/client/src/services/file.ts index 9a9af4b1..6676c242 100644 --- a/packages/client/src/services/file.ts +++ b/packages/client/src/services/file.ts @@ -4,40 +4,22 @@ import SparkMD5 from 'spark-md5'; import { HttpClient } from './http-client'; const splitBigFile = (file: File): Promise<{ chunks: File[]; md5: string }> => { - return new Promise((resolve, reject) => { - const spark = new SparkMD5.ArrayBuffer(); - const fileReader = new FileReader(); + return new Promise((resolve) => { const chunks = []; const len = Math.ceil(file.size / FILE_CHUNK_SIZE); - let current = 0; - - fileReader.onload = (e) => { - current++; - - const chunk = e.target.result; - spark.append(chunk); - - if (current < len) { - loadChunk(); - } else { - resolve({ chunks, md5: spark.end() }); - } + const sparkWorker = new Worker(new URL('./spark-md5.js', import.meta.url)); + sparkWorker.onmessage = (evt) => { + resolve({ md5: evt.data.md5, chunks }); }; - fileReader.onerror = (err) => { - reject(err); - }; - - const loadChunk = () => { - const start = current * FILE_CHUNK_SIZE; + for (let i = 0; i < len; i++) { + const start = i * FILE_CHUNK_SIZE; const end = Math.min(start + FILE_CHUNK_SIZE, file.size); const chunk = file.slice(start, end); - chunks.push(chunk); - fileReader.readAsArrayBuffer(chunk); - }; + } - loadChunk(); + sparkWorker.postMessage({ chunks }); }); }; @@ -74,12 +56,21 @@ const uploadFileToServer = (arg: { }); }; -export const uploadFile = async (file: File, onUploadProgress?: (progress: number) => void) => { +export const uploadFile = async ( + file: File, + onUploadProgress?: (progress: number) => void, + onTooLarge?: () => void +) => { const wraponUploadProgress = (percent) => { return onUploadProgress && onUploadProgress(Math.ceil(percent * 100)); }; const filename = file.name; + + if (file.size > FILE_CHUNK_SIZE * 5) { + onTooLarge && onTooLarge(); + } + if (file.size <= FILE_CHUNK_SIZE) { const spark = new SparkMD5.ArrayBuffer(); spark.append(file); @@ -91,33 +82,57 @@ export const uploadFile = async (file: File, onUploadProgress?: (progress: numbe const unitPercent = 1 / chunks.length; const progressMap = {}; - await Promise.all( - chunks.map((chunk, index) => - uploadFileToServer({ - filename, - file: chunk, - chunkIndex: index + 1, - md5, - isChunk: true, - onUploadProgress: (progress) => { - progressMap[index] = progress * unitPercent; - wraponUploadProgress( - Object.keys(progressMap).reduce((a, c) => { - return (a += progressMap[c]); - }, 0) - ); - }, - }) - ) - ); - const url = await HttpClient.request({ - method: FileApiDefinition.mergeChunk.method, - url: FileApiDefinition.mergeChunk.client(), - params: { - filename, - md5, + /** + * 先上传一块分块,如果文件已上传,即无需上传后续分块 + */ + let url = await uploadFileToServer({ + filename, + file: chunks[0], + chunkIndex: 1, + md5, + isChunk: true, + onUploadProgress: (progress) => { + progressMap[0] = progress * unitPercent; + wraponUploadProgress( + Object.keys(progressMap).reduce((a, c) => { + return (a += progressMap[c]); + }, 0) + ); }, }); + + if (!url) { + await Promise.all( + chunks.slice(1).map((chunk, index) => + uploadFileToServer({ + filename, + file: chunk, + chunkIndex: index + 1 + 1, + md5, + isChunk: true, + onUploadProgress: (progress) => { + progressMap[index + 1] = progress * unitPercent; + wraponUploadProgress( + Object.keys(progressMap).reduce((a, c) => { + return (a += progressMap[c]); + }, 0) + ); + }, + }) + ) + ); + url = await HttpClient.request({ + method: FileApiDefinition.mergeChunk.method, + url: FileApiDefinition.mergeChunk.client(), + params: { + filename, + md5, + }, + }); + } else { + wraponUploadProgress(1); + } + return url; } }; diff --git a/packages/client/src/services/spark-md5.js b/packages/client/src/services/spark-md5.js new file mode 100644 index 00000000..f93fdddd --- /dev/null +++ b/packages/client/src/services/spark-md5.js @@ -0,0 +1,31 @@ +import SparkMD5 from 'spark-md5'; + +addEventListener('message', (e) => { + const chunks = e.data.chunks || []; + + if (!chunks.length) return; + + const spark = new SparkMD5.ArrayBuffer(); + const reader = new FileReader(); + let index = 0; + + const load = () => { + const chunk = chunks[index]; + reader.readAsArrayBuffer(chunk); + }; + + reader.onload = (e) => { + spark.append(e.target.result); + + if (index === chunks.length - 1) { + const md5 = spark.end(); + postMessage({ md5 }); + self.close(); + } else { + index++; + load(); + } + }; + + load(); +}); diff --git a/packages/client/src/tiptap/core/wrappers/attachment/index.tsx b/packages/client/src/tiptap/core/wrappers/attachment/index.tsx index afdc7788..9346ef9f 100644 --- a/packages/client/src/tiptap/core/wrappers/attachment/index.tsx +++ b/packages/client/src/tiptap/core/wrappers/attachment/index.tsx @@ -1,5 +1,5 @@ import { IconClose, IconDownload, IconPlayCircle } from '@douyinfe/semi-icons'; -import { Button, Collapsible, Progress, Space, Spin, Typography } from '@douyinfe/semi-ui'; +import { Button, Collapsible, Progress, Space, Spin, Toast, Typography } from '@douyinfe/semi-ui'; import { FILE_CHUNK_SIZE } from '@think/domains'; import { NodeViewWrapper } from '@tiptap/react'; import cls from 'classnames'; @@ -46,7 +46,9 @@ export const AttachmentWrapper = ({ editor, node, updateAttributes }) => { } try { - const url = await uploadFile(file, setUploadProgress); + const url = await uploadFile(file, setUploadProgress, () => { + Toast.info('文件较大,文件将在后台进行上传处理,您可继续其他操作'); + }); updateAttributes({ ...fileInfo, url }); toggleLoading(false); setUploadProgress(0); diff --git a/packages/client/tsconfig.json b/packages/client/tsconfig.json index 0cc3693c..025a791a 100644 --- a/packages/client/tsconfig.json +++ b/packages/client/tsconfig.json @@ -31,6 +31,6 @@ "thirtypart/*": ["thirtypart/*"] } }, - "include": ["next-env.d.ts", "global.d.ts", "**/*.ts", "**/*.tsx"], + "include": ["next-env.d.ts", "global.d.ts", "**/*.ts", "**/*.tsx", "src/services/spark-md5.js"], "exclude": ["node_modules", "next.config.js"] }