|
@@ -1,69 +1,240 @@
|
|
|
import TTUploader from 'tt-uploader'
|
|
import TTUploader from 'tt-uploader'
|
|
|
import { HsyAssumeRoleService } from '@/api/course/userVideo'
|
|
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) => {
|
|
export const uploadToHSY = async (file, onProgress, type, callBackUp) => {
|
|
|
|
|
+ console.log('=== 火山云上传开始 ===')
|
|
|
|
|
+ console.log('文件信息:', { name: file.name, size: file.size, type: file.type })
|
|
|
|
|
+
|
|
|
try {
|
|
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
|
|
const credentials = res.data.result.credentials
|
|
|
- //console.log('火山云 credentials 凭证:', credentials)
|
|
|
|
|
|
|
+ console.log('凭证信息:', {
|
|
|
|
|
+ accessKeyId: credentials.accessKeyId,
|
|
|
|
|
+ currentTime: credentials.currentTime,
|
|
|
|
|
+ expiredTime: credentials.expiredTime
|
|
|
|
|
+ })
|
|
|
|
|
|
|
|
if (!credentials) {
|
|
if (!credentials) {
|
|
|
throw new Error('未获取到火山云 STS 凭证')
|
|
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 date = new Date();
|
|
|
const yyyy = date.getFullYear();
|
|
const yyyy = date.getFullYear();
|
|
|
const MM = String(date.getMonth() + 1).padStart(2, '0');
|
|
const MM = String(date.getMonth() + 1).padStart(2, '0');
|
|
|
const dd = String(date.getDate()).padStart(2, '0');
|
|
const dd = String(date.getDate()).padStart(2, '0');
|
|
|
const datePath = `${yyyy}${MM}${dd}`;
|
|
const datePath = `${yyyy}${MM}${dd}`;
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
const fileName = `${Date.now()}.mp4`;
|
|
const fileName = `${Date.now()}.mp4`;
|
|
|
-
|
|
|
|
|
const remoteFileName = `course/${datePath}/${fileName}`;
|
|
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) => {
|
|
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) => {
|
|
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) => {
|
|
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) => {
|
|
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,
|
|
fileKey,
|
|
|
cancel: () => {
|
|
cancel: () => {
|
|
|
try {
|
|
try {
|
|
|
|
|
+ console.log('取消上传:', fileKey)
|
|
|
|
|
+ clearTimeout(timeoutId)
|
|
|
|
|
+ uploader.off('progress')
|
|
|
|
|
+ uploader.off('complete')
|
|
|
|
|
+ uploader.off('error')
|
|
|
uploader.removeFile(fileKey)
|
|
uploader.removeFile(fileKey)
|
|
|
- reject(new Error('Upload cancelled by user'))
|
|
|
|
|
|
|
+ const error = new Error('用户取消上传')
|
|
|
|
|
+ error.type = 'cancelled'
|
|
|
|
|
+ reject(error)
|
|
|
} catch (e) {
|
|
} catch (e) {
|
|
|
|
|
+ console.error('取消上传失败:', e)
|
|
|
reject(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) {
|
|
} 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
|
|
throw error
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|