Browse Source

fix:修改文件上传为分片上传

lalala 1 year ago
parent
commit
5b9b893cf7
4 changed files with 118 additions and 11 deletions
  1. 6 0
      package-lock.json
  2. 1 0
      package.json
  3. 83 0
      src/apis/file.ts
  4. 28 11
      src/views/AiSpeakingPage/SpeakingPage.vue

+ 6 - 0
package-lock.json

@@ -21,6 +21,7 @@
         "mitt": "^3.0.1",
         "pinia": "^2.1.7",
         "pinia-plugin-persistedstate": "^3.2.1",
+        "spark-md5": "^3.0.2",
         "vue": "^3.4.29",
         "vue-demi": "^0.14.10",
         "vue-router": "^4.3.3",
@@ -3952,6 +3953,11 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/spark-md5": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmmirror.com/spark-md5/-/spark-md5-3.0.2.tgz",
+      "integrity": "sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw=="
+    },
     "node_modules/ssf": {
       "version": "0.11.2",
       "resolved": "https://registry.npmmirror.com/ssf/-/ssf-0.11.2.tgz",

+ 1 - 0
package.json

@@ -25,6 +25,7 @@
     "mitt": "^3.0.1",
     "pinia": "^2.1.7",
     "pinia-plugin-persistedstate": "^3.2.1",
+    "spark-md5": "^3.0.2",
     "vue": "^3.4.29",
     "vue-demi": "^0.14.10",
     "vue-router": "^4.3.3",

+ 83 - 0
src/apis/file.ts

@@ -1,4 +1,7 @@
+
+import { ElMessage } from 'element-plus';
 import axiosInstance from "@/apis/axios.config";
+import SparkMD5 from 'spark-md5'
 
 const FILE_PREFIX = "/api/file"
 
@@ -13,6 +16,86 @@ const fileApis = {
             timeout:300000 // 添加超时时间等待
         });
     },
+    async uploadChunkFile(file, userId, assignmentId, duration) {
+        const chunkCount = Math.ceil(file.size / chunk_size)
+        // 创建文件hash
+        const md5 = await createFileMd5(file)
+        // 创建文件分片
+        const chunkList = createChunkFileList(file)
+        // 创建分片对象
+        const chunkListObj = createChunksFileObj(chunkList, file, md5, chunkCount)
+        const promises =  uploadChunkFile(chunkListObj, userId, assignmentId, duration)
+        await Promise.all(promises)
+    }
+
+   
+}
+
+
+const createFileMd5 = (file) =>{
+    const reader = new FileReader()
+    return new Promise((resolve, reject) => {
+        reader.onload = (e) =>{
+            const md5 = SparkMD5.ArrayBuffer.hash(e.target.result)
+            resolve(md5)
+        }
+        reader.onerror = (error) => {
+            reject(error)
+        }
+        reader.readAsArrayBuffer(file)
+    })
+
+}
+const chunk_size = 5 * 1024 * 1024
+ // 创建文件分片
+ const createChunkFileList = (file) => {
+    let current = 0
+    const chunkList = []
+    while(current < file.size){
+        chunkList.push(file.slice(current, Math.min(current + chunk_size, file.size)))
+        current += chunk_size
+    }
+    return chunkList
 }
 
+const createChunksFileObj = (chunkList, file, md5, chunkCount) => {
+    return chunkList.map(((chunk, index) => {
+        return {
+            file: chunk,
+            hash: md5,
+            name: file.name,
+            index:index,
+            chunkCount
+        }
+    }))
+    
+}
+
+const uploadChunkFile = (chunkListObj, userId, assignmentId, duration) => {
+
+    return chunkListObj.map(async(chunk) =>{
+        try{
+            const formData = new FormData();
+            formData.append("file", chunk.file)
+            formData.append("fileName", chunk.name)
+            formData.append('fileMd5', chunk.hash)
+            formData.append('chunkCount', chunk.chunkCount)
+            formData.append('currentIndex', chunk.index)
+            formData.append('userId', userId)
+            formData.append('assignmentId', assignmentId)
+            formData.append('duration', duration)
+            return  axiosInstance.post(`${FILE_PREFIX}/large`, formData, {
+                headers: {
+                    'Content-Type': 'multipart/form-data'
+                }
+            });
+            
+        } catch (error) {
+            return Promise.reject(error)
+        }
+    })
+}
+
+
+
 export default fileApis

+ 28 - 11
src/views/AiSpeakingPage/SpeakingPage.vue

@@ -704,22 +704,38 @@ const saveHomework = async () => {
         const videoBlob = new Blob([...recordedChunks.value], { type: "video/webm" });
         const fileName = `user_${userId}_${assignmentId}_${Date.now()}`;
         let renameFile =new File([videoBlob], fileName +'.webm', { type: 'video/webm' })
-        try {
-          doubaoApis
-          .saveSigment(userId, assignmentId,renameFile, duration)
-          .then(res =>{
-            recordedChunks.value = [];
-            isHomeworkSaved.value = true;
-            hasHomeworkSaved.value = true;
-            isHomeworkStarted.value = false;
-            ElMessage.success("视频片段暂存成功");
-            loadingInstance.close();
-          })
+        try{
+          fileApis.uploadChunkFile(renameFile, userId, assignmentId, duration)
+            .then(res => {
+                recordedChunks.value = [];
+                isHomeworkSaved.value = true;
+                hasHomeworkSaved.value = true;
+                isHomeworkStarted.value = false;
+                ElMessage.success("视频片段暂存成功");
+                loadingInstance.close();
+            })
         } catch (error) {
           console.error("视频片段上传失败:", error);
           ElMessage.error("视频片段上传失败");
           loadingInstance.close();
         }
+
+        // try {
+        //   doubaoApis
+        //   .saveSigment(userId, assignmentId,renameFile, duration)
+        //   .then(res =>{
+        //     recordedChunks.value = [];
+        //     isHomeworkSaved.value = true;
+        //     hasHomeworkSaved.value = true;
+        //     isHomeworkStarted.value = false;
+        //     ElMessage.success("视频片段暂存成功");
+        //     loadingInstance.close();
+        //   })
+        // } catch (error) {
+        //   console.error("视频片段上传失败:", error);
+        //   ElMessage.error("视频片段上传失败");
+        //   loadingInstance.close();
+        // }
       };
     } else {
       ElMessage.error('状态出错啦')
@@ -826,6 +842,7 @@ const resetHomework = async() => {
       isHomeworkSaved.value = false;
       isHomeworkStarted.value = false;
       initRecognitionValue()
+      recordingDuration.value = 0;
       ElMessage.success("作业内容已清空");
       getHistory()