Explorar el Código

视频上传代码提交

yjwang hace 1 semana
padre
commit
9fbeaba72c
Se han modificado 2 ficheros con 261 adiciones y 41 borrados
  1. 239 38
      src/utils/hsy.js
  2. 22 3
      src/views/course/videoResource/index.vue

+ 239 - 38
src/utils/hsy.js

@@ -1,69 +1,240 @@
 import TTUploader from 'tt-uploader'
 import { HsyAssumeRoleService } from '@/api/course/userVideo'
-const  spaceName = process.env.VUE_APP_HSY_SPACE
+const spaceName = process.env.VUE_APP_HSY_SPACE
+
 export const uploadToHSY = async (file, onProgress, type, callBackUp) => {
+  console.log('=== 火山云上传开始 ===')
+  console.log('文件信息:', { name: file.name, size: file.size, type: file.type })
+  
   try {
-    const res = await HsyAssumeRoleService()
-    //console.log('火山云 STS 凭证:', res)
+    // 获取 STS 凭证
+    console.log('1. 获取火山云 STS 凭证...')
+    let res
+    try {
+      res = await HsyAssumeRoleService()
+      console.log('STS 凭证响应:', res)
+    } catch (stsError) {
+      console.error('获取 STS 凭证失败:', stsError)
+      const error = new Error(`获取火山云凭证失败: ${stsError.message || '网络错误'}`)
+      error.originalError = stsError
+      throw error
+    }
+    
+    if (!res || !res.data || !res.data.result) {
+      const error = new Error('STS 凭证响应格式错误')
+      error.responseData = res
+      console.error('STS 凭证响应格式错误:', res)
+      throw error
+    }
+    
     const credentials = res.data.result.credentials
-    //console.log('火山云 credentials 凭证:', credentials)
+    console.log('凭证信息:', {
+      accessKeyId: credentials.accessKeyId,
+      currentTime: credentials.currentTime,
+      expiredTime: credentials.expiredTime
+    })
 
     if (!credentials) {
       throw new Error('未获取到火山云 STS 凭证')
     }
 
-    const uploader = new TTUploader({
-      appId: '917052',
-      userId: '68185444',
-      videoConfig: {
-        spaceName: spaceName,
-      }
-    })
+    // 初始化上传器,配置分块上传支持大文件
+    console.log('2. 初始化 TTUploader,配置分块上传...')
+    let uploader
+    try {
+      uploader = new TTUploader({
+        appId: '917052',
+        userId: '68185444',
+        videoConfig: {
+          spaceName: spaceName,
+        },
+        // 分块上传配置
+        chunked: true,
+        chunkSize: 2 * 1024 * 1024, // 2MB 每块
+        retryCount: 5, // 失败重试次数
+        retryInterval: 2000, // 重试间隔2秒
+        concurrentRequestLimit: 2, // 并发上传数
+        timeout: 60000, // 单个分块超时60秒
+      })
+      console.log('TTUploader 初始化成功')
+    } catch (initError) {
+      console.error('TTUploader 初始化失败:', initError)
+      const error = new Error(`上传器初始化失败: ${initError.message || '未知错误'}`)
+      error.originalError = initError
+      throw error
+    }
 
+    // 生成文件路径
     const date = new Date();
     const yyyy = date.getFullYear();
     const MM = String(date.getMonth() + 1).padStart(2, '0');
     const dd = String(date.getDate()).padStart(2, '0');
     const datePath = `${yyyy}${MM}${dd}`;
-
-
     const fileName = `${Date.now()}.mp4`;
-
     const remoteFileName = `course/${datePath}/${fileName}`;
+    console.log('3. 上传文件路径:', remoteFileName)
 
-    const fileKey = uploader.addFile({
-      file,
-      type: 'video',
-      fileName:remoteFileName,
-      stsToken: {
-        CurrentTime: credentials.currentTime,
-        ExpiredTime: credentials.expiredTime,
-        SessionToken: credentials.sessionToken,
-        AccessKeyID: credentials.accessKeyId,
-        SecretAccessKey: credentials.secretAccessKey,
-      },
-    })
+    // 添加文件到上传队列
+    console.log('4. 添加文件到上传队列...')
+    let fileKey
+    try {
+      fileKey = uploader.addFile({
+        file,
+        type: 'video',
+        fileName: remoteFileName,
+        stsToken: {
+          CurrentTime: credentials.currentTime,
+          ExpiredTime: credentials.expiredTime,
+          SessionToken: credentials.sessionToken,
+          AccessKeyID: credentials.accessKeyId,
+          SecretAccessKey: credentials.secretAccessKey,
+        },
+      })
+      console.log('文件添加成功,fileKey:', fileKey)
+    } catch (addFileError) {
+      console.error('添加文件失败:', addFileError)
+      const error = new Error(`添加文件到上传队列失败: ${addFileError.message || '未知错误'}`)
+      error.originalError = addFileError
+      throw error
+    }
 
     return new Promise((resolve, reject) => {
+      // 动态计算总超时时间
+      const fileSizeInMB = file.size / (1024 * 1024);
+      const baseTimeout = 300000; // 300秒
+      const maxTimeout = 1800000; // 1800秒
+      const uploadTimeout = Math.min(baseTimeout + (fileSizeInMB * 10000), maxTimeout);
+      console.log(`文件大小:${fileSizeInMB.toFixed(2)}MB,总超时时间:${uploadTimeout / 1000}秒`)
+      
+      const timeoutId = setTimeout(() => {
+        console.error('=== 上传总超时 ===')
+        console.error('超时文件:', fileKey)
+        console.error('文件大小:', fileSizeInMB.toFixed(2), 'MB')
+        console.error('超时时间:', uploadTimeout / 1000, '秒')
+        
+        try {
+          uploader.removeFile(fileKey)
+        } catch (e) {
+          console.error('移除文件失败:', e)
+        }
+        
+        const timeoutError = new Error(`上传超时(${uploadTimeout / 1000}秒),请检查网络连接。文件大小:${fileSizeInMB.toFixed(2)}MB`)
+        timeoutError.type = 'timeout'
+        timeoutError.fileSize = fileSizeInMB
+        timeoutError.timeout = uploadTimeout
+        
+        if (onProgress) {
+          onProgress({
+            percent: 0,
+            status: 'failed',
+            error: timeoutError.message
+          })
+        }
+        reject(timeoutError)
+      }, uploadTimeout)
 
       // 上传进度
       uploader.on('progress', (info) => {
-        onProgress && onProgress({
-          percent: info.percent,
-        })
+        console.log('上传进度:', info)
+        if (onProgress && info && typeof info.percent === 'number') {
+          onProgress({
+            percent: info.percent,
+            status: 'uploading'
+          })
+        }
       })
 
       // 上传完成
       uploader.on('complete', (info) => {
-        resolve({
-          ...info.uploadResult,
-          fileKey,
-        })
+        console.log('=== 上传完成 ===')
+        console.log('上传结果:', info)
+        clearTimeout(timeoutId)
+        // 清理事件监听器
+        uploader.off('progress')
+        uploader.off('complete')
+        uploader.off('error')
+        
+        if (onProgress && info) {
+          onProgress({
+            percent: 1,
+            status: 'success'
+          })
+        }
+        
+        if (info && info.uploadResult) {
+          resolve({
+            ...info.uploadResult,
+            fileKey,
+          })
+        } else {
+          const error = new Error('上传完成但返回数据格式错误')
+          error.type = 'data_format_error'
+          error.uploadResult = info
+          console.error('返回数据格式错误:', info)
+          reject(error)
+        }
       })
 
-      // 上传失败
+      // 上传失败 - 这是最重要的异常捕获
       uploader.on('error', (info) => {
-        reject(info)
+        console.error('=== 上传失败 ===')
+        console.error('错误详情:', info)
+        console.error('错误类型:', typeof info)
+        
+        clearTimeout(timeoutId)
+        // 清理事件监听器
+        uploader.off('progress')
+        uploader.off('complete')
+        uploader.off('error')
+        
+        // 构造详细的错误信息
+        let errorMessage = '上传失败'
+        let errorType = 'upload_error'
+        
+        if (info) {
+          if (typeof info === 'string') {
+            errorMessage = info
+          } else if (info.message) {
+            errorMessage = info.message
+          } else if (info.error) {
+            errorMessage = info.error
+          } else if (info.msg) {
+            errorMessage = info.msg
+          } else if (info.errMsg) {
+            errorMessage = info.errMsg
+          } else {
+            errorMessage = JSON.stringify(info)
+          }
+          
+          // 判断错误类型
+          if (errorMessage.includes('timeout') || errorMessage.includes('超时')) {
+            errorType = 'chunk_timeout'
+          } else if (errorMessage.includes('network') || errorMessage.includes('网络')) {
+            errorType = 'network_error'
+          } else if (errorMessage.includes('401') || errorMessage.includes('403')) {
+            errorType = 'auth_error'
+          } else if (errorMessage.includes('500') || errorMessage.includes('502') || errorMessage.includes('503')) {
+            errorType = 'server_error'
+          }
+        }
+        
+        const error = new Error(`火山云上传失败: ${errorMessage}`)
+        error.type = errorType
+        error.originalError = info
+        error.fileKey = fileKey
+        error.fileSize = fileSizeInMB
+        
+        console.error('错误类型:', errorType)
+        console.error('错误信息:', errorMessage)
+        
+        if (onProgress) {
+          onProgress({
+            percent: 0,
+            status: 'failed',
+            error: errorMessage
+          })
+        }
+        reject(error)
       })
 
       // 取消上传支持
@@ -73,20 +244,50 @@ export const uploadToHSY = async (file, onProgress, type, callBackUp) => {
           fileKey,
           cancel: () => {
             try {
+              console.log('取消上传:', fileKey)
+              clearTimeout(timeoutId)
+              uploader.off('progress')
+              uploader.off('complete')
+              uploader.off('error')
               uploader.removeFile(fileKey)
-              reject(new Error('Upload cancelled by user'))
+              const error = new Error('用户取消上传')
+              error.type = 'cancelled'
+              reject(error)
             } catch (e) {
+              console.error('取消上传失败:', e)
               reject(e)
             }
           }
         })
       }
 
-      uploader.start(fileKey)
+      // 开始上传
+      console.log('5. 开始上传...')
+      try {
+        uploader.start(fileKey)
+      } catch (startError) {
+        console.error('启动上传失败:', startError)
+        clearTimeout(timeoutId)
+        const error = new Error(`启动上传失败: ${startError.message || '未知错误'}`)
+        error.type = 'start_error'
+        error.originalError = startError
+        reject(error)
+      }
     })
 
   } catch (error) {
-    console.error('火山云上传失败:', error)
+    console.error('=== 火山云上传异常 ===')
+    console.error('异常类型:', error.type || 'unknown')
+    console.error('异常信息:', error.message)
+    console.error('原始异常:', error.originalError || error)
+    
+    if (onProgress) {
+      onProgress({
+        percent: 0,
+        status: 'failed',
+        error: error.message
+      })
+    }
     throw error
   }
 }

+ 22 - 3
src/views/course/videoResource/index.vue

@@ -1357,15 +1357,25 @@ export default {
     // },
     //上传火山云
     async uploadVideoToHsy(file, form, onProgress) {
+      console.log('=== 线路2火山云上传开始 ===')
+      console.log('文件信息:', { name: file.name, size: file.size })
+      
       try {
         this.updateUploadProgress('line2Status', 'uploading');
 
         const data = await uploadToHSY(
           file,
           (progress) => {
+            // 检查 progress 参数
+            if (!progress) {
+              console.warn('收到空的 progress 参数')
+              return
+            }
+            
             // 火山云的进度是小数0-1
             if (typeof progress.percent === 'number') {
               const percent = Math.floor(progress.percent * 100);
+              console.log('线路2上传进度:', percent + '%')
 
               // 更新线路2进度
               this.updateUploadProgress('line2', percent);
@@ -1381,16 +1391,19 @@ export default {
 
             // 状态同步(成功 / 失败)
             if (progress.status === 'success') {
+              console.log('线路2上传成功')
               this.updateUploadProgress('line2Status', 'success');
               this.updateUploadProgress('line2', 100);
             }
 
             if (progress.status === 'failed') {
+              console.log('线路2上传失败')
               this.updateUploadProgress('line2Status', 'failed');
             }
           },
           1,
           (uploadInfo) => {
+            console.log('获取上传信息:', uploadInfo)
             if (form.tempId) {
               const tokens = this.uploadCancellationTokens.get(form.tempId) || {};
               tokens.hsy = uploadInfo.cancel;
@@ -1398,18 +1411,24 @@ export default {
             }
           }
         );
-        console.log("上传火山云返回参数",data)
+        console.log("上传火山云返回参数", data)
+
+        // 检查返回数据
+        if (!data || !data.SourceInfo || !data.SourceInfo.FileName) {
+          throw new Error('火山云返回数据格式错误');
+        }
 
         form.line2 = `${process.env.VUE_APP_VIDEO_URL}/${data.SourceInfo.FileName}`;
         this.form.hsyVid = data.Vid
         this.form.hsyVodUrl = process.env.VUE_APP_VIDEO_URL+"/"+data.SourceInfo.FileName
-        console.log("this.form",this.form)
+        console.log("更新表单数据", this.form)
         this.$message.success('线路二上传成功');
         return { success: true, url: form.line2 };
 
       } catch (error) {
+        console.error('线路2上传失败:', error)
         this.updateUploadProgress('line2Status', 'failed');
-        this.$message.error('线路二上传失败');
+        this.$message.error('线路二上传失败' + (error?.message || '未知错误'));
         return { success: false, error: error?.message || 'upload failed' };
       }
     },