@alibarbar/common 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -249,7 +249,7 @@ var ChunkUploader = class {
249
249
  this.taskId = initResponse.taskId;
250
250
  if (initResponse.instantUpload && initResponse.fileUrl) {
251
251
  this.status = "completed" /* COMPLETED */;
252
- const result2 = {
252
+ const result = {
253
253
  taskId: this.taskId,
254
254
  fileUrl: initResponse.fileUrl,
255
255
  fileName: this.file.name,
@@ -258,8 +258,8 @@ var ChunkUploader = class {
258
258
  success: true,
259
259
  message: "\u6587\u4EF6\u5DF2\u5B58\u5728\uFF0C\u79D2\u4F20\u6210\u529F"
260
260
  };
261
- this.options.onComplete(result2);
262
- return result2;
261
+ this.options.onComplete(result);
262
+ return result;
263
263
  }
264
264
  this.prepareChunks();
265
265
  const existingChunks = await this.getUploadedChunks();
@@ -273,14 +273,13 @@ var ChunkUploader = class {
273
273
  );
274
274
  }
275
275
  const progress = await this.getUploadProgress();
276
- if (!progress || progress.percentage < 100) {
277
- const current = progress ? progress.percentage : 0;
278
- throw new Error(`\u4E0A\u4F20\u672A\u5B8C\u6210\uFF1A\u5F53\u524D\u8FDB\u5EA6 ${current}%`);
276
+ if (progress?.percentage === 100) {
277
+ const result = await this.completeUpload();
278
+ this.status = "completed" /* COMPLETED */;
279
+ this.options.onComplete(result);
280
+ return result;
279
281
  }
280
- const result = await this.completeUpload();
281
- this.status = "completed" /* COMPLETED */;
282
- this.options.onComplete(result);
283
- return result;
282
+ return null;
284
283
  } catch (error) {
285
284
  this.status = "failed" /* FAILED */;
286
285
  const err = error instanceof Error ? error : new Error(String(error));
@@ -366,5 +365,5 @@ function createUploader(file, options) {
366
365
  }
367
366
 
368
367
  export { ChunkUploader, UploadStatus, createUploader };
369
- //# sourceMappingURL=chunk-RZAVU7BP.mjs.map
370
- //# sourceMappingURL=chunk-RZAVU7BP.mjs.map
368
+ //# sourceMappingURL=chunk-3CSXZMP7.mjs.map
369
+ //# sourceMappingURL=chunk-3CSXZMP7.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types/upload.ts","../src/browser/upload.ts"],"names":["UploadStatus"],"mappings":"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","file":"chunk-3CSXZMP7.mjs","sourcesContent":["/**\n * 文件上传相关类型定义\n */\n\n/**\n * 上传初始化请求参数\n */\nexport interface UploadInitRequest {\n fileName: string;\n fileSize: number;\n fileMd5: string;\n chunkSize?: number;\n}\n\n/**\n * 上传初始化响应\n */\nexport interface UploadInitResponse {\n taskId: string;\n totalChunks: number;\n existingChunks: number[];\n instantUpload: boolean;\n fileUrl?: string;\n chunkSize: number;\n}\n\n/**\n * 分片上传响应\n */\nexport interface ChunkUploadResponse {\n chunkIndex: number;\n success: boolean;\n message: string;\n chunkMd5: string;\n}\n\n/**\n * 完成上传响应\n */\nexport interface CompleteUploadResponse {\n taskId: string;\n fileUrl: string;\n fileName: string;\n fileSize: number;\n fileMd5: string;\n success: boolean;\n message: string;\n}\n\n/**\n * 上传进度信息\n */\nexport interface UploadProgress {\n taskId: string;\n fileName: string;\n fileSize: number;\n totalChunks: number;\n uploadedChunks: number;\n percentage: number;\n status: string;\n uploadedSize: number;\n}\n\n/**\n * API响应包装类型\n */\nexport interface ApiResponse<T> {\n code: number;\n message: string;\n data: T;\n timestamp: number;\n}\n\n/**\n * 上传配置选项\n */\nexport interface UploadOptions {\n /** 分片大小(字节),默认 2MB */\n chunkSize?: number;\n /** 并发上传数量,默认 3 */\n concurrency?: number;\n /** 重试次数,默认 3 */\n retryCount?: number;\n /** 重试延迟(毫秒),默认 1000 */\n retryDelay?: number;\n /** 基础API地址 */\n baseURL?: string;\n /** 请求头 */\n headers?: Record<string, string>;\n /** 上传进度回调 */\n onProgress?: (progress: UploadProgress) => void;\n /** 上传完成回调 */\n onComplete?: (result: CompleteUploadResponse) => void;\n /** 上传错误回调 */\n onError?: (error: Error) => void;\n}\n\n/**\n * 上传状态\n */\nexport enum UploadStatus {\n PENDING = 'pending',\n UPLOADING = 'uploading',\n PAUSED = 'paused',\n COMPLETED = 'completed',\n FAILED = 'failed',\n CANCELLED = 'cancelled',\n}\n\n/**\n * 文件分片信息\n */\nexport interface ChunkInfo {\n index: number;\n start: number;\n end: number;\n blob: Blob;\n md5?: string;\n}\n","/**\n * 文件分片上传工具类\n * 支持分片上传、断点续传、进度追踪\n */\n\nimport {\n UploadOptions,\n UploadInitRequest,\n UploadInitResponse,\n ChunkUploadResponse,\n CompleteUploadResponse,\n UploadProgress,\n UploadStatus,\n ChunkInfo,\n ApiResponse,\n} from '../types/upload';\nimport { calculateBlobMD5, splitFileIntoChunks } from './file';\n\n/**\n * 文件上传器类\n */\nexport class ChunkUploader {\n private file: File;\n private options: Required<UploadOptions>;\n private taskId: string | null = null;\n private chunks: ChunkInfo[] = [];\n private uploadedChunks: Set<number> = new Set();\n private status: UploadStatus = UploadStatus.PENDING;\n private abortController: AbortController | null = null;\n private initResponse: UploadInitResponse | null = null;\n\n constructor(file: File, options: UploadOptions = {}) {\n this.file = file;\n this.options = {\n chunkSize: options.chunkSize || 2 * 1024 * 1024, // 默认2MB\n concurrency: options.concurrency || 3,\n retryCount: options.retryCount || 3,\n retryDelay: options.retryDelay || 1000,\n baseURL: options.baseURL || '',\n headers: options.headers || {},\n onProgress: options.onProgress || (() => {}),\n onComplete: options.onComplete || (() => {}),\n onError: options.onError || (() => {}),\n };\n }\n\n /**\n * 初始化上传\n */\n private async initUpload(): Promise<UploadInitResponse> {\n const fileMd5 = await this.calculateFileMD5();\n const request: UploadInitRequest = {\n fileName: this.file.name,\n fileSize: this.file.size,\n fileMd5,\n chunkSize: this.options.chunkSize,\n };\n\n const response = await this.request<ApiResponse<UploadInitResponse>>('/api/files/common/init', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...this.options.headers,\n },\n body: JSON.stringify(request),\n });\n\n if (response.code !== 200) {\n throw new Error(response.message || '初始化上传失败');\n }\n\n return response.data;\n }\n\n /**\n * 计算文件MD5(简化版,实际应该使用spark-md5等库)\n */\n private async calculateFileMD5(): Promise<string> {\n // 这里使用简化的方法,实际项目中应该使用spark-md5库\n const chunks = splitFileIntoChunks(this.file, this.options.chunkSize);\n const md5Promises = chunks.map((chunk, index) =>\n calculateBlobMD5(chunk).then(md5 => ({ index, md5 }))\n );\n const md5Results = await Promise.all(md5Promises);\n // 简单拼接,实际应该使用正确的MD5算法\n return md5Results.map(r => r.md5).join('');\n }\n\n /**\n * 准备分片\n */\n private prepareChunks(): void {\n const blobChunks = splitFileIntoChunks(this.file, this.options.chunkSize);\n this.chunks = blobChunks.map((blob, index) => ({\n index,\n start: index * this.options.chunkSize,\n end: Math.min((index + 1) * this.options.chunkSize, this.file.size),\n blob,\n }));\n }\n\n /**\n * 获取已上传的分片列表\n */\n private async getUploadedChunks(): Promise<number[]> {\n if (!this.taskId) return [];\n\n try {\n const response = await this.request<ApiResponse<number[]>>(\n `/api/files/common/chunks/${this.taskId}`,\n {\n method: 'GET',\n headers: this.options.headers,\n }\n );\n\n if (response.code === 200) {\n return response.data || [];\n }\n } catch (error) {\n console.warn('获取已上传分片失败:', error);\n }\n\n return [];\n }\n\n /**\n * 上传单个分片\n */\n private async uploadChunk(chunkInfo: ChunkInfo, retryCount = 0): Promise<void> {\n if (!this.taskId) {\n throw new Error('任务ID不存在');\n }\n\n if (this.uploadedChunks.has(chunkInfo.index)) {\n return; // 已上传,跳过\n }\n\n try {\n // 计算分片MD5\n const chunkMd5 = await calculateBlobMD5(chunkInfo.blob);\n\n const formData = new FormData();\n formData.append('file', chunkInfo.blob, this.file.name);\n\n const url = `/api/files/common/chunk?taskId=${this.taskId}&chunkIndex=${chunkInfo.index}&chunkMd5=${chunkMd5}`;\n\n const response = await this.request<ApiResponse<ChunkUploadResponse>>(url, {\n method: 'POST',\n headers: this.options.headers,\n body: formData,\n });\n\n if (response.code === 200 && response.data.success) {\n this.uploadedChunks.add(chunkInfo.index);\n this.updateProgress();\n } else {\n throw new Error(response.message || '分片上传失败');\n }\n } catch (error) {\n if (retryCount < this.options.retryCount) {\n await this.delay(this.options.retryDelay);\n return this.uploadChunk(chunkInfo, retryCount + 1);\n }\n throw error;\n }\n }\n\n /**\n * 并发上传分片\n */\n private async uploadChunksConcurrently(): Promise<void> {\n const chunksToUpload = this.chunks.filter(chunk => !this.uploadedChunks.has(chunk.index));\n\n if (chunksToUpload.length === 0) {\n return; // 所有分片已上传\n }\n\n // 使用信号量控制并发数\n let currentIndex = 0;\n const semaphore = new Array(this.options.concurrency).fill(null);\n const allUploadPromises: Promise<void>[] = [];\n\n const uploadNext = async (): Promise<void> => {\n while (this.status === UploadStatus.UPLOADING) {\n // 原子性地获取下一个分片索引\n const chunkIndex = currentIndex++;\n if (chunkIndex >= chunksToUpload.length) {\n break; // 没有更多分片需要上传\n }\n\n const chunk = chunksToUpload[chunkIndex];\n // 创建上传 Promise 并添加到总数组中\n const uploadPromise = this.uploadChunk(chunk);\n allUploadPromises.push(uploadPromise);\n\n // 等待当前分片上传完成(这样可以控制并发数)\n await uploadPromise;\n }\n };\n\n // 启动并发上传任务(最多 concurrency 个并发)\n const concurrencyPromises = semaphore.map(() => uploadNext());\n\n // 等待所有并发任务完成\n await Promise.all(concurrencyPromises);\n\n // 确保所有上传 Promise 都已完成(双重保险)\n await Promise.all(allUploadPromises);\n\n // 最终验证所有分片都已上传\n const totalChunks = this.chunks.length;\n const uploadedCount = this.uploadedChunks.size;\n if (uploadedCount < totalChunks) {\n const missingChunks = this.chunks\n .map((chunk, index) => (!this.uploadedChunks.has(index) ? index : null))\n .filter(index => index !== null);\n throw new Error(\n `上传未完成:已上传 ${uploadedCount}/${totalChunks} 个分片,缺失分片索引: ${missingChunks.join(', ')}`\n );\n }\n }\n\n /**\n * 获取上传进度(用于验证上传是否完成)\n */\n private async getUploadProgress(): Promise<UploadProgress | null> {\n if (!this.taskId) {\n return null;\n }\n\n try {\n const response = await this.request<ApiResponse<UploadProgress>>(\n `/api/files/common/progress/${this.taskId}`,\n {\n method: 'GET',\n headers: this.options.headers,\n }\n );\n\n if (response.code === 200) {\n return response.data;\n }\n } catch (error) {\n console.warn('获取上传进度失败:', error);\n }\n\n return null;\n }\n\n /**\n * 完成上传(调用后端合并分片接口)\n *\n * 只有在进度为 100% 时才会被调用\n */\n private async completeUpload(): Promise<CompleteUploadResponse> {\n if (!this.taskId) {\n throw new Error('任务ID不存在');\n }\n\n const response = await this.request<ApiResponse<CompleteUploadResponse>>(\n `/api/files/common/complete/${this.taskId}`,\n {\n method: 'POST',\n headers: this.options.headers,\n }\n );\n\n if (response.code !== 200) {\n throw new Error(response.message || '完成上传失败');\n }\n\n return response.data;\n }\n\n /**\n * 更新上传进度(仅用于回调前端显示)\n */\n private async updateProgress(): Promise<void> {\n if (!this.taskId) return;\n\n try {\n const response = await this.request<ApiResponse<UploadProgress>>(\n `/api/files/common/progress/${this.taskId}`,\n {\n method: 'GET',\n headers: this.options.headers,\n }\n );\n\n if (response.code === 200 && this.options.onProgress) {\n this.options.onProgress(response.data);\n }\n } catch (error) {\n console.warn('获取上传进度失败:', error);\n }\n }\n\n /**\n * 开始上传\n */\n async upload(): Promise<CompleteUploadResponse> {\n try {\n this.status = UploadStatus.UPLOADING;\n this.abortController = new AbortController();\n\n // 1. 初始化上传\n const initResponse = await this.initUpload();\n this.taskId = initResponse.taskId;\n\n // 如果已经完成上传(秒传)\n if (initResponse.instantUpload && initResponse.fileUrl) {\n this.status = UploadStatus.COMPLETED;\n const result: CompleteUploadResponse = {\n taskId: this.taskId,\n fileUrl: initResponse.fileUrl,\n fileName: this.file.name,\n fileSize: this.file.size,\n fileMd5: '',\n success: true,\n message: '文件已存在,秒传成功',\n };\n this.options.onComplete(result);\n return result;\n }\n\n // 2. 准备分片\n this.prepareChunks();\n\n // 3. 获取已上传的分片(断点续传)\n const existingChunks = await this.getUploadedChunks();\n existingChunks.forEach(index => this.uploadedChunks.add(index));\n\n // 4. 上传分片\n await this.uploadChunksConcurrently();\n\n // 5. 再次验证所有分片都已上传(双重检查)\n const totalChunks = this.chunks.length;\n const uploadedCount = this.uploadedChunks.size;\n if (uploadedCount < totalChunks) {\n throw new Error(\n `上传验证失败:已上传 ${uploadedCount}/${totalChunks} 个分片,无法完成上传`\n );\n }\n\n // 6. 获取最终上传进度,确认100%完成\n const progress = await this.getUploadProgress();\n if (progress?.percentage === 100) {\n const result = await this.completeUpload();\n this.status = UploadStatus.COMPLETED;\n this.options.onComplete(result);\n return result;\n }\n\n return null as unknown as CompleteUploadResponse;\n } catch (error) {\n this.status = UploadStatus.FAILED;\n const err = error instanceof Error ? error : new Error(String(error));\n this.options.onError(err);\n throw err;\n }\n }\n\n /**\n * 暂停上传\n */\n pause(): void {\n if (this.status === UploadStatus.UPLOADING) {\n this.status = UploadStatus.PAUSED;\n if (this.abortController) {\n this.abortController.abort();\n }\n }\n }\n\n /**\n * 恢复上传\n */\n async resume(): Promise<CompleteUploadResponse> {\n if (this.status === UploadStatus.PAUSED) {\n return this.upload();\n }\n throw new Error('当前状态无法恢复上传');\n }\n\n /**\n * 取消上传\n */\n async cancel(): Promise<void> {\n if (this.taskId && this.status === UploadStatus.UPLOADING) {\n try {\n await this.request<ApiResponse<null>>(`/api/files/common/cancel/${this.taskId}`, {\n method: 'POST',\n headers: this.options.headers,\n });\n } catch (error) {\n console.warn('取消上传失败:', error);\n }\n }\n\n this.status = UploadStatus.CANCELLED;\n if (this.abortController) {\n this.abortController.abort();\n }\n }\n\n /**\n * 获取当前状态\n */\n getStatus(): UploadStatus {\n return this.status;\n }\n\n /**\n * 获取任务ID\n */\n getTaskId(): string | null {\n return this.taskId;\n }\n\n /**\n * HTTP请求封装\n */\n private async request<T>(url: string, options: RequestInit = {}): Promise<T> {\n const fullUrl = `${this.options.baseURL}${url}`;\n const signal = this.abortController?.signal;\n\n const response = await fetch(fullUrl, {\n ...options,\n signal,\n });\n\n if (!response.ok) {\n throw new Error(`HTTP错误: ${response.status} ${response.statusText}`);\n }\n\n return response.json();\n }\n\n /**\n * 延迟函数\n */\n private delay(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n}\n\n/**\n * 创建文件上传器实例\n * @param file - 文件对象\n * @param options - 上传配置选项\n * @returns 上传器实例\n */\nexport function createUploader(file: File, options?: UploadOptions): ChunkUploader {\n return new ChunkUploader(file, options);\n}\n"]}
@@ -251,7 +251,7 @@ var ChunkUploader = class {
251
251
  this.taskId = initResponse.taskId;
252
252
  if (initResponse.instantUpload && initResponse.fileUrl) {
253
253
  this.status = "completed" /* COMPLETED */;
254
- const result2 = {
254
+ const result = {
255
255
  taskId: this.taskId,
256
256
  fileUrl: initResponse.fileUrl,
257
257
  fileName: this.file.name,
@@ -260,8 +260,8 @@ var ChunkUploader = class {
260
260
  success: true,
261
261
  message: "\u6587\u4EF6\u5DF2\u5B58\u5728\uFF0C\u79D2\u4F20\u6210\u529F"
262
262
  };
263
- this.options.onComplete(result2);
264
- return result2;
263
+ this.options.onComplete(result);
264
+ return result;
265
265
  }
266
266
  this.prepareChunks();
267
267
  const existingChunks = await this.getUploadedChunks();
@@ -275,14 +275,13 @@ var ChunkUploader = class {
275
275
  );
276
276
  }
277
277
  const progress = await this.getUploadProgress();
278
- if (!progress || progress.percentage < 100) {
279
- const current = progress ? progress.percentage : 0;
280
- throw new Error(`\u4E0A\u4F20\u672A\u5B8C\u6210\uFF1A\u5F53\u524D\u8FDB\u5EA6 ${current}%`);
278
+ if (progress?.percentage === 100) {
279
+ const result = await this.completeUpload();
280
+ this.status = "completed" /* COMPLETED */;
281
+ this.options.onComplete(result);
282
+ return result;
281
283
  }
282
- const result = await this.completeUpload();
283
- this.status = "completed" /* COMPLETED */;
284
- this.options.onComplete(result);
285
- return result;
284
+ return null;
286
285
  } catch (error) {
287
286
  this.status = "failed" /* FAILED */;
288
287
  const err = error instanceof Error ? error : new Error(String(error));
@@ -370,5 +369,5 @@ function createUploader(file, options) {
370
369
  exports.ChunkUploader = ChunkUploader;
371
370
  exports.UploadStatus = UploadStatus;
372
371
  exports.createUploader = createUploader;
373
- //# sourceMappingURL=chunk-TADFN7AL.js.map
374
- //# sourceMappingURL=chunk-TADFN7AL.js.map
372
+ //# sourceMappingURL=chunk-SZ6F6CDP.js.map
373
+ //# sourceMappingURL=chunk-SZ6F6CDP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types/upload.ts","../src/browser/upload.ts"],"names":["UploadStatus","splitFileIntoChunks","calculateBlobMD5"],"mappings":"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","file":"chunk-SZ6F6CDP.js","sourcesContent":["/**\n * 文件上传相关类型定义\n */\n\n/**\n * 上传初始化请求参数\n */\nexport interface UploadInitRequest {\n fileName: string;\n fileSize: number;\n fileMd5: string;\n chunkSize?: number;\n}\n\n/**\n * 上传初始化响应\n */\nexport interface UploadInitResponse {\n taskId: string;\n totalChunks: number;\n existingChunks: number[];\n instantUpload: boolean;\n fileUrl?: string;\n chunkSize: number;\n}\n\n/**\n * 分片上传响应\n */\nexport interface ChunkUploadResponse {\n chunkIndex: number;\n success: boolean;\n message: string;\n chunkMd5: string;\n}\n\n/**\n * 完成上传响应\n */\nexport interface CompleteUploadResponse {\n taskId: string;\n fileUrl: string;\n fileName: string;\n fileSize: number;\n fileMd5: string;\n success: boolean;\n message: string;\n}\n\n/**\n * 上传进度信息\n */\nexport interface UploadProgress {\n taskId: string;\n fileName: string;\n fileSize: number;\n totalChunks: number;\n uploadedChunks: number;\n percentage: number;\n status: string;\n uploadedSize: number;\n}\n\n/**\n * API响应包装类型\n */\nexport interface ApiResponse<T> {\n code: number;\n message: string;\n data: T;\n timestamp: number;\n}\n\n/**\n * 上传配置选项\n */\nexport interface UploadOptions {\n /** 分片大小(字节),默认 2MB */\n chunkSize?: number;\n /** 并发上传数量,默认 3 */\n concurrency?: number;\n /** 重试次数,默认 3 */\n retryCount?: number;\n /** 重试延迟(毫秒),默认 1000 */\n retryDelay?: number;\n /** 基础API地址 */\n baseURL?: string;\n /** 请求头 */\n headers?: Record<string, string>;\n /** 上传进度回调 */\n onProgress?: (progress: UploadProgress) => void;\n /** 上传完成回调 */\n onComplete?: (result: CompleteUploadResponse) => void;\n /** 上传错误回调 */\n onError?: (error: Error) => void;\n}\n\n/**\n * 上传状态\n */\nexport enum UploadStatus {\n PENDING = 'pending',\n UPLOADING = 'uploading',\n PAUSED = 'paused',\n COMPLETED = 'completed',\n FAILED = 'failed',\n CANCELLED = 'cancelled',\n}\n\n/**\n * 文件分片信息\n */\nexport interface ChunkInfo {\n index: number;\n start: number;\n end: number;\n blob: Blob;\n md5?: string;\n}\n","/**\n * 文件分片上传工具类\n * 支持分片上传、断点续传、进度追踪\n */\n\nimport {\n UploadOptions,\n UploadInitRequest,\n UploadInitResponse,\n ChunkUploadResponse,\n CompleteUploadResponse,\n UploadProgress,\n UploadStatus,\n ChunkInfo,\n ApiResponse,\n} from '../types/upload';\nimport { calculateBlobMD5, splitFileIntoChunks } from './file';\n\n/**\n * 文件上传器类\n */\nexport class ChunkUploader {\n private file: File;\n private options: Required<UploadOptions>;\n private taskId: string | null = null;\n private chunks: ChunkInfo[] = [];\n private uploadedChunks: Set<number> = new Set();\n private status: UploadStatus = UploadStatus.PENDING;\n private abortController: AbortController | null = null;\n private initResponse: UploadInitResponse | null = null;\n\n constructor(file: File, options: UploadOptions = {}) {\n this.file = file;\n this.options = {\n chunkSize: options.chunkSize || 2 * 1024 * 1024, // 默认2MB\n concurrency: options.concurrency || 3,\n retryCount: options.retryCount || 3,\n retryDelay: options.retryDelay || 1000,\n baseURL: options.baseURL || '',\n headers: options.headers || {},\n onProgress: options.onProgress || (() => {}),\n onComplete: options.onComplete || (() => {}),\n onError: options.onError || (() => {}),\n };\n }\n\n /**\n * 初始化上传\n */\n private async initUpload(): Promise<UploadInitResponse> {\n const fileMd5 = await this.calculateFileMD5();\n const request: UploadInitRequest = {\n fileName: this.file.name,\n fileSize: this.file.size,\n fileMd5,\n chunkSize: this.options.chunkSize,\n };\n\n const response = await this.request<ApiResponse<UploadInitResponse>>('/api/files/common/init', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...this.options.headers,\n },\n body: JSON.stringify(request),\n });\n\n if (response.code !== 200) {\n throw new Error(response.message || '初始化上传失败');\n }\n\n return response.data;\n }\n\n /**\n * 计算文件MD5(简化版,实际应该使用spark-md5等库)\n */\n private async calculateFileMD5(): Promise<string> {\n // 这里使用简化的方法,实际项目中应该使用spark-md5库\n const chunks = splitFileIntoChunks(this.file, this.options.chunkSize);\n const md5Promises = chunks.map((chunk, index) =>\n calculateBlobMD5(chunk).then(md5 => ({ index, md5 }))\n );\n const md5Results = await Promise.all(md5Promises);\n // 简单拼接,实际应该使用正确的MD5算法\n return md5Results.map(r => r.md5).join('');\n }\n\n /**\n * 准备分片\n */\n private prepareChunks(): void {\n const blobChunks = splitFileIntoChunks(this.file, this.options.chunkSize);\n this.chunks = blobChunks.map((blob, index) => ({\n index,\n start: index * this.options.chunkSize,\n end: Math.min((index + 1) * this.options.chunkSize, this.file.size),\n blob,\n }));\n }\n\n /**\n * 获取已上传的分片列表\n */\n private async getUploadedChunks(): Promise<number[]> {\n if (!this.taskId) return [];\n\n try {\n const response = await this.request<ApiResponse<number[]>>(\n `/api/files/common/chunks/${this.taskId}`,\n {\n method: 'GET',\n headers: this.options.headers,\n }\n );\n\n if (response.code === 200) {\n return response.data || [];\n }\n } catch (error) {\n console.warn('获取已上传分片失败:', error);\n }\n\n return [];\n }\n\n /**\n * 上传单个分片\n */\n private async uploadChunk(chunkInfo: ChunkInfo, retryCount = 0): Promise<void> {\n if (!this.taskId) {\n throw new Error('任务ID不存在');\n }\n\n if (this.uploadedChunks.has(chunkInfo.index)) {\n return; // 已上传,跳过\n }\n\n try {\n // 计算分片MD5\n const chunkMd5 = await calculateBlobMD5(chunkInfo.blob);\n\n const formData = new FormData();\n formData.append('file', chunkInfo.blob, this.file.name);\n\n const url = `/api/files/common/chunk?taskId=${this.taskId}&chunkIndex=${chunkInfo.index}&chunkMd5=${chunkMd5}`;\n\n const response = await this.request<ApiResponse<ChunkUploadResponse>>(url, {\n method: 'POST',\n headers: this.options.headers,\n body: formData,\n });\n\n if (response.code === 200 && response.data.success) {\n this.uploadedChunks.add(chunkInfo.index);\n this.updateProgress();\n } else {\n throw new Error(response.message || '分片上传失败');\n }\n } catch (error) {\n if (retryCount < this.options.retryCount) {\n await this.delay(this.options.retryDelay);\n return this.uploadChunk(chunkInfo, retryCount + 1);\n }\n throw error;\n }\n }\n\n /**\n * 并发上传分片\n */\n private async uploadChunksConcurrently(): Promise<void> {\n const chunksToUpload = this.chunks.filter(chunk => !this.uploadedChunks.has(chunk.index));\n\n if (chunksToUpload.length === 0) {\n return; // 所有分片已上传\n }\n\n // 使用信号量控制并发数\n let currentIndex = 0;\n const semaphore = new Array(this.options.concurrency).fill(null);\n const allUploadPromises: Promise<void>[] = [];\n\n const uploadNext = async (): Promise<void> => {\n while (this.status === UploadStatus.UPLOADING) {\n // 原子性地获取下一个分片索引\n const chunkIndex = currentIndex++;\n if (chunkIndex >= chunksToUpload.length) {\n break; // 没有更多分片需要上传\n }\n\n const chunk = chunksToUpload[chunkIndex];\n // 创建上传 Promise 并添加到总数组中\n const uploadPromise = this.uploadChunk(chunk);\n allUploadPromises.push(uploadPromise);\n\n // 等待当前分片上传完成(这样可以控制并发数)\n await uploadPromise;\n }\n };\n\n // 启动并发上传任务(最多 concurrency 个并发)\n const concurrencyPromises = semaphore.map(() => uploadNext());\n\n // 等待所有并发任务完成\n await Promise.all(concurrencyPromises);\n\n // 确保所有上传 Promise 都已完成(双重保险)\n await Promise.all(allUploadPromises);\n\n // 最终验证所有分片都已上传\n const totalChunks = this.chunks.length;\n const uploadedCount = this.uploadedChunks.size;\n if (uploadedCount < totalChunks) {\n const missingChunks = this.chunks\n .map((chunk, index) => (!this.uploadedChunks.has(index) ? index : null))\n .filter(index => index !== null);\n throw new Error(\n `上传未完成:已上传 ${uploadedCount}/${totalChunks} 个分片,缺失分片索引: ${missingChunks.join(', ')}`\n );\n }\n }\n\n /**\n * 获取上传进度(用于验证上传是否完成)\n */\n private async getUploadProgress(): Promise<UploadProgress | null> {\n if (!this.taskId) {\n return null;\n }\n\n try {\n const response = await this.request<ApiResponse<UploadProgress>>(\n `/api/files/common/progress/${this.taskId}`,\n {\n method: 'GET',\n headers: this.options.headers,\n }\n );\n\n if (response.code === 200) {\n return response.data;\n }\n } catch (error) {\n console.warn('获取上传进度失败:', error);\n }\n\n return null;\n }\n\n /**\n * 完成上传(调用后端合并分片接口)\n *\n * 只有在进度为 100% 时才会被调用\n */\n private async completeUpload(): Promise<CompleteUploadResponse> {\n if (!this.taskId) {\n throw new Error('任务ID不存在');\n }\n\n const response = await this.request<ApiResponse<CompleteUploadResponse>>(\n `/api/files/common/complete/${this.taskId}`,\n {\n method: 'POST',\n headers: this.options.headers,\n }\n );\n\n if (response.code !== 200) {\n throw new Error(response.message || '完成上传失败');\n }\n\n return response.data;\n }\n\n /**\n * 更新上传进度(仅用于回调前端显示)\n */\n private async updateProgress(): Promise<void> {\n if (!this.taskId) return;\n\n try {\n const response = await this.request<ApiResponse<UploadProgress>>(\n `/api/files/common/progress/${this.taskId}`,\n {\n method: 'GET',\n headers: this.options.headers,\n }\n );\n\n if (response.code === 200 && this.options.onProgress) {\n this.options.onProgress(response.data);\n }\n } catch (error) {\n console.warn('获取上传进度失败:', error);\n }\n }\n\n /**\n * 开始上传\n */\n async upload(): Promise<CompleteUploadResponse> {\n try {\n this.status = UploadStatus.UPLOADING;\n this.abortController = new AbortController();\n\n // 1. 初始化上传\n const initResponse = await this.initUpload();\n this.taskId = initResponse.taskId;\n\n // 如果已经完成上传(秒传)\n if (initResponse.instantUpload && initResponse.fileUrl) {\n this.status = UploadStatus.COMPLETED;\n const result: CompleteUploadResponse = {\n taskId: this.taskId,\n fileUrl: initResponse.fileUrl,\n fileName: this.file.name,\n fileSize: this.file.size,\n fileMd5: '',\n success: true,\n message: '文件已存在,秒传成功',\n };\n this.options.onComplete(result);\n return result;\n }\n\n // 2. 准备分片\n this.prepareChunks();\n\n // 3. 获取已上传的分片(断点续传)\n const existingChunks = await this.getUploadedChunks();\n existingChunks.forEach(index => this.uploadedChunks.add(index));\n\n // 4. 上传分片\n await this.uploadChunksConcurrently();\n\n // 5. 再次验证所有分片都已上传(双重检查)\n const totalChunks = this.chunks.length;\n const uploadedCount = this.uploadedChunks.size;\n if (uploadedCount < totalChunks) {\n throw new Error(\n `上传验证失败:已上传 ${uploadedCount}/${totalChunks} 个分片,无法完成上传`\n );\n }\n\n // 6. 获取最终上传进度,确认100%完成\n const progress = await this.getUploadProgress();\n if (progress?.percentage === 100) {\n const result = await this.completeUpload();\n this.status = UploadStatus.COMPLETED;\n this.options.onComplete(result);\n return result;\n }\n\n return null as unknown as CompleteUploadResponse;\n } catch (error) {\n this.status = UploadStatus.FAILED;\n const err = error instanceof Error ? error : new Error(String(error));\n this.options.onError(err);\n throw err;\n }\n }\n\n /**\n * 暂停上传\n */\n pause(): void {\n if (this.status === UploadStatus.UPLOADING) {\n this.status = UploadStatus.PAUSED;\n if (this.abortController) {\n this.abortController.abort();\n }\n }\n }\n\n /**\n * 恢复上传\n */\n async resume(): Promise<CompleteUploadResponse> {\n if (this.status === UploadStatus.PAUSED) {\n return this.upload();\n }\n throw new Error('当前状态无法恢复上传');\n }\n\n /**\n * 取消上传\n */\n async cancel(): Promise<void> {\n if (this.taskId && this.status === UploadStatus.UPLOADING) {\n try {\n await this.request<ApiResponse<null>>(`/api/files/common/cancel/${this.taskId}`, {\n method: 'POST',\n headers: this.options.headers,\n });\n } catch (error) {\n console.warn('取消上传失败:', error);\n }\n }\n\n this.status = UploadStatus.CANCELLED;\n if (this.abortController) {\n this.abortController.abort();\n }\n }\n\n /**\n * 获取当前状态\n */\n getStatus(): UploadStatus {\n return this.status;\n }\n\n /**\n * 获取任务ID\n */\n getTaskId(): string | null {\n return this.taskId;\n }\n\n /**\n * HTTP请求封装\n */\n private async request<T>(url: string, options: RequestInit = {}): Promise<T> {\n const fullUrl = `${this.options.baseURL}${url}`;\n const signal = this.abortController?.signal;\n\n const response = await fetch(fullUrl, {\n ...options,\n signal,\n });\n\n if (!response.ok) {\n throw new Error(`HTTP错误: ${response.status} ${response.statusText}`);\n }\n\n return response.json();\n }\n\n /**\n * 延迟函数\n */\n private delay(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n}\n\n/**\n * 创建文件上传器实例\n * @param file - 文件对象\n * @param options - 上传配置选项\n * @returns 上传器实例\n */\nexport function createUploader(file: File, options?: UploadOptions): ChunkUploader {\n return new ChunkUploader(file, options);\n}\n"]}
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ var chunkJQZBPAPO_js = require('./chunk-JQZBPAPO.js');
6
6
  var chunk56W6YECK_js = require('./chunk-56W6YECK.js');
7
7
  var chunkY364QIQH_js = require('./chunk-Y364QIQH.js');
8
8
  var chunkOX5PLOWB_js = require('./chunk-OX5PLOWB.js');
9
- var chunkTADFN7AL_js = require('./chunk-TADFN7AL.js');
9
+ var chunkSZ6F6CDP_js = require('./chunk-SZ6F6CDP.js');
10
10
  var chunk7V5UQXIO_js = require('./chunk-7V5UQXIO.js');
11
11
  var chunkXVUE53T3_js = require('./chunk-XVUE53T3.js');
12
12
  var chunkDYBSRI7V_js = require('./chunk-DYBSRI7V.js');
@@ -225,15 +225,15 @@ Object.defineProperty(exports, "translate", {
225
225
  });
226
226
  Object.defineProperty(exports, "ChunkUploader", {
227
227
  enumerable: true,
228
- get: function () { return chunkTADFN7AL_js.ChunkUploader; }
228
+ get: function () { return chunkSZ6F6CDP_js.ChunkUploader; }
229
229
  });
230
230
  Object.defineProperty(exports, "UploadStatus", {
231
231
  enumerable: true,
232
- get: function () { return chunkTADFN7AL_js.UploadStatus; }
232
+ get: function () { return chunkSZ6F6CDP_js.UploadStatus; }
233
233
  });
234
234
  Object.defineProperty(exports, "createUploader", {
235
235
  enumerable: true,
236
- get: function () { return chunkTADFN7AL_js.createUploader; }
236
+ get: function () { return chunkSZ6F6CDP_js.createUploader; }
237
237
  });
238
238
  Object.defineProperty(exports, "calculateBlobMD5", {
239
239
  enumerable: true,
package/dist/index.mjs CHANGED
@@ -4,7 +4,7 @@ export { Queue, batch, debounce, memoize, once, retry, throttle, timeout } from
4
4
  export { Tracker, createTracker, flush, getTracker, initTracker, setCommonParams, setUserInfo, trackClick, trackEvent, trackExposure, trackPageView } from './chunk-PJ7UCTX4.mjs';
5
5
  export { contrast, darken, hexToRgb, hslToRgb, lighten, mix, rgbToHex, rgbToHsl } from './chunk-QIOC54LQ.mjs';
6
6
  export { createTranslator, formatCurrencyI18n, formatDateI18n, formatNumberI18n, formatRelativeTime, getLocale, pluralize, translate } from './chunk-QIBE7GVN.mjs';
7
- export { ChunkUploader, UploadStatus, createUploader } from './chunk-RZAVU7BP.mjs';
7
+ export { ChunkUploader, UploadStatus, createUploader } from './chunk-3CSXZMP7.mjs';
8
8
  export { calculateBlobMD5, calculateFileMD5, formatFileSize, getFileExtension, getFileNameWithoutExtension, splitFileIntoChunks } from './chunk-ZVJ6NQUM.mjs';
9
9
  export { cookie, getStorageKeyPair, localStorage, sessionStorage, setStorageKeyPair, storage } from './chunk-HME2N3VY.mjs';
10
10
  export { base64Decode, base64Encode, exportPrivateKey, exportPublicKey, generateRSAKeyPair, generateRandomString, generateUUID, hash, importPrivateKey, importPublicKey, rsaDecrypt, rsaEncrypt, sha256 } from './chunk-HLDFI7R2.mjs';
package/dist/upload.js CHANGED
@@ -1,17 +1,17 @@
1
1
  'use strict';
2
2
 
3
- var chunkTADFN7AL_js = require('./chunk-TADFN7AL.js');
3
+ var chunkSZ6F6CDP_js = require('./chunk-SZ6F6CDP.js');
4
4
  require('./chunk-7V5UQXIO.js');
5
5
 
6
6
 
7
7
 
8
8
  Object.defineProperty(exports, "ChunkUploader", {
9
9
  enumerable: true,
10
- get: function () { return chunkTADFN7AL_js.ChunkUploader; }
10
+ get: function () { return chunkSZ6F6CDP_js.ChunkUploader; }
11
11
  });
12
12
  Object.defineProperty(exports, "createUploader", {
13
13
  enumerable: true,
14
- get: function () { return chunkTADFN7AL_js.createUploader; }
14
+ get: function () { return chunkSZ6F6CDP_js.createUploader; }
15
15
  });
16
16
  //# sourceMappingURL=upload.js.map
17
17
  //# sourceMappingURL=upload.js.map
package/dist/upload.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { ChunkUploader, createUploader } from './chunk-RZAVU7BP.mjs';
1
+ export { ChunkUploader, createUploader } from './chunk-3CSXZMP7.mjs';
2
2
  import './chunk-ZVJ6NQUM.mjs';
3
3
  //# sourceMappingURL=upload.mjs.map
4
4
  //# sourceMappingURL=upload.mjs.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alibarbar/common",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "Alibarbar 通用工具库",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/types/upload.ts","../src/browser/upload.ts"],"names":["UploadStatus","result"],"mappings":";;;AAoGO,IAAK,YAAA,qBAAAA,aAAAA,KAAL;AACL,EAAAA,cAAA,SAAA,CAAA,GAAU,SAAA;AACV,EAAAA,cAAA,WAAA,CAAA,GAAY,WAAA;AACZ,EAAAA,cAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,cAAA,WAAA,CAAA,GAAY,WAAA;AACZ,EAAAA,cAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,cAAA,WAAA,CAAA,GAAY,WAAA;AANF,EAAA,OAAAA,aAAAA;AAAA,CAAA,EAAA,YAAA,IAAA,EAAA;;;AC/EL,IAAM,gBAAN,MAAoB;AAAA,EAUzB,WAAA,CAAY,IAAA,EAAY,OAAA,GAAyB,EAAC,EAAG;AAPrD,IAAA,IAAA,CAAQ,MAAA,GAAwB,IAAA;AAChC,IAAA,IAAA,CAAQ,SAAsB,EAAC;AAC/B,IAAA,IAAA,CAAQ,cAAA,uBAAkC,GAAA,EAAI;AAC9C,IAAA,IAAA,CAAQ,MAAA,GAAA,SAAA;AACR,IAAA,IAAA,CAAQ,eAAA,GAA0C,IAAA;AAClD,IAAA,IAAA,CAAQ,YAAA,GAA0C,IAAA;AAGhD,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,IAAA,IAAA,CAAK,OAAA,GAAU;AAAA,MACb,SAAA,EAAW,OAAA,CAAQ,SAAA,IAAa,CAAA,GAAI,IAAA,GAAO,IAAA;AAAA;AAAA,MAC3C,WAAA,EAAa,QAAQ,WAAA,IAAe,CAAA;AAAA,MACpC,UAAA,EAAY,QAAQ,UAAA,IAAc,CAAA;AAAA,MAClC,UAAA,EAAY,QAAQ,UAAA,IAAc,GAAA;AAAA,MAClC,OAAA,EAAS,QAAQ,OAAA,IAAW,EAAA;AAAA,MAC5B,OAAA,EAAS,OAAA,CAAQ,OAAA,IAAW,EAAC;AAAA,MAC7B,UAAA,EAAY,OAAA,CAAQ,UAAA,KAAe,MAAM;AAAA,MAAC,CAAA,CAAA;AAAA,MAC1C,UAAA,EAAY,OAAA,CAAQ,UAAA,KAAe,MAAM;AAAA,MAAC,CAAA,CAAA;AAAA,MAC1C,OAAA,EAAS,OAAA,CAAQ,OAAA,KAAY,MAAM;AAAA,MAAC,CAAA;AAAA,KACtC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,UAAA,GAA0C;AACtD,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,gBAAA,EAAiB;AAC5C,IAAA,MAAM,OAAA,GAA6B;AAAA,MACjC,QAAA,EAAU,KAAK,IAAA,CAAK,IAAA;AAAA,MACpB,QAAA,EAAU,KAAK,IAAA,CAAK,IAAA;AAAA,MACpB,OAAA;AAAA,MACA,SAAA,EAAW,KAAK,OAAA,CAAQ;AAAA,KAC1B;AAEA,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,OAAA,CAAyC,wBAAA,EAA0B;AAAA,MAC7F,MAAA,EAAQ,MAAA;AAAA,MACR,OAAA,EAAS;AAAA,QACP,cAAA,EAAgB,kBAAA;AAAA,QAChB,GAAG,KAAK,OAAA,CAAQ;AAAA,OAClB;AAAA,MACA,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,OAAO;AAAA,KAC7B,CAAA;AAED,IAAA,IAAI,QAAA,CAAS,SAAS,GAAA,EAAK;AACzB,MAAA,MAAM,IAAI,KAAA,CAAM,QAAA,CAAS,OAAA,IAAW,4CAAS,CAAA;AAAA,IAC/C;AAEA,IAAA,OAAO,QAAA,CAAS,IAAA;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,gBAAA,GAAoC;AAEhD,IAAA,MAAM,SAAS,mBAAA,CAAoB,IAAA,CAAK,IAAA,EAAM,IAAA,CAAK,QAAQ,SAAS,CAAA;AACpE,IAAA,MAAM,cAAc,MAAA,CAAO,GAAA;AAAA,MAAI,CAAC,KAAA,EAAO,KAAA,KACrC,gBAAA,CAAiB,KAAK,CAAA,CAAE,IAAA,CAAK,CAAA,GAAA,MAAQ,EAAE,KAAA,EAAO,GAAA,EAAI,CAAE;AAAA,KACtD;AACA,IAAA,MAAM,UAAA,GAAa,MAAM,OAAA,CAAQ,GAAA,CAAI,WAAW,CAAA;AAEhD,IAAA,OAAO,WAAW,GAAA,CAAI,CAAA,CAAA,KAAK,EAAE,GAAG,CAAA,CAAE,KAAK,EAAE,CAAA;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAA,GAAsB;AAC5B,IAAA,MAAM,aAAa,mBAAA,CAAoB,IAAA,CAAK,IAAA,EAAM,IAAA,CAAK,QAAQ,SAAS,CAAA;AACxE,IAAA,IAAA,CAAK,MAAA,GAAS,UAAA,CAAW,GAAA,CAAI,CAAC,MAAM,KAAA,MAAW;AAAA,MAC7C,KAAA;AAAA,MACA,KAAA,EAAO,KAAA,GAAQ,IAAA,CAAK,OAAA,CAAQ,SAAA;AAAA,MAC5B,GAAA,EAAK,IAAA,CAAK,GAAA,CAAA,CAAK,KAAA,GAAQ,CAAA,IAAK,KAAK,OAAA,CAAQ,SAAA,EAAW,IAAA,CAAK,IAAA,CAAK,IAAI,CAAA;AAAA,MAClE;AAAA,KACF,CAAE,CAAA;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,iBAAA,GAAuC;AACnD,IAAA,IAAI,CAAC,IAAA,CAAK,MAAA,EAAQ,OAAO,EAAC;AAE1B,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,OAAA;AAAA,QAC1B,CAAA,yBAAA,EAA4B,KAAK,MAAM,CAAA,CAAA;AAAA,QACvC;AAAA,UACE,MAAA,EAAQ,KAAA;AAAA,UACR,OAAA,EAAS,KAAK,OAAA,CAAQ;AAAA;AACxB,OACF;AAEA,MAAA,IAAI,QAAA,CAAS,SAAS,GAAA,EAAK;AACzB,QAAA,OAAO,QAAA,CAAS,QAAQ,EAAC;AAAA,MAC3B;AAAA,IACF,SAAS,KAAA,EAAO;AACd,MAAA,OAAA,CAAQ,IAAA,CAAK,2DAAc,KAAK,CAAA;AAAA,IAClC;AAEA,IAAA,OAAO,EAAC;AAAA,EACV;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,WAAA,CAAY,SAAA,EAAsB,UAAA,GAAa,CAAA,EAAkB;AAC7E,IAAA,IAAI,CAAC,KAAK,MAAA,EAAQ;AAChB,MAAA,MAAM,IAAI,MAAM,kCAAS,CAAA;AAAA,IAC3B;AAEA,IAAA,IAAI,IAAA,CAAK,cAAA,CAAe,GAAA,CAAI,SAAA,CAAU,KAAK,CAAA,EAAG;AAC5C,MAAA;AAAA,IACF;AAEA,IAAA,IAAI;AAEF,MAAA,MAAM,QAAA,GAAW,MAAM,gBAAA,CAAiB,SAAA,CAAU,IAAI,CAAA;AAEtD,MAAA,MAAM,QAAA,GAAW,IAAI,QAAA,EAAS;AAC9B,MAAA,QAAA,CAAS,OAAO,MAAA,EAAQ,SAAA,CAAU,IAAA,EAAM,IAAA,CAAK,KAAK,IAAI,CAAA;AAEtD,MAAA,MAAM,GAAA,GAAM,kCAAkC,IAAA,CAAK,MAAM,eAAe,SAAA,CAAU,KAAK,aAAa,QAAQ,CAAA,CAAA;AAE5G,MAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,OAAA,CAA0C,GAAA,EAAK;AAAA,QACzE,MAAA,EAAQ,MAAA;AAAA,QACR,OAAA,EAAS,KAAK,OAAA,CAAQ,OAAA;AAAA,QACtB,IAAA,EAAM;AAAA,OACP,CAAA;AAED,MAAA,IAAI,QAAA,CAAS,IAAA,KAAS,GAAA,IAAO,QAAA,CAAS,KAAK,OAAA,EAAS;AAClD,QAAA,IAAA,CAAK,cAAA,CAAe,GAAA,CAAI,SAAA,CAAU,KAAK,CAAA;AACvC,QAAA,IAAA,CAAK,cAAA,EAAe;AAAA,MACtB,CAAA,MAAO;AACL,QAAA,MAAM,IAAI,KAAA,CAAM,QAAA,CAAS,OAAA,IAAW,sCAAQ,CAAA;AAAA,MAC9C;AAAA,IACF,SAAS,KAAA,EAAO;AACd,MAAA,IAAI,UAAA,GAAa,IAAA,CAAK,OAAA,CAAQ,UAAA,EAAY;AACxC,QAAA,MAAM,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,OAAA,CAAQ,UAAU,CAAA;AACxC,QAAA,OAAO,IAAA,CAAK,WAAA,CAAY,SAAA,EAAW,UAAA,GAAa,CAAC,CAAA;AAAA,MACnD;AACA,MAAA,MAAM,KAAA;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,wBAAA,GAA0C;AACtD,IAAA,MAAM,cAAA,GAAiB,IAAA,CAAK,MAAA,CAAO,MAAA,CAAO,CAAA,KAAA,KAAS,CAAC,IAAA,CAAK,cAAA,CAAe,GAAA,CAAI,KAAA,CAAM,KAAK,CAAC,CAAA;AAExF,IAAA,IAAI,cAAA,CAAe,WAAW,CAAA,EAAG;AAC/B,MAAA;AAAA,IACF;AAGA,IAAA,IAAI,YAAA,GAAe,CAAA;AACnB,IAAA,MAAM,SAAA,GAAY,IAAI,KAAA,CAAM,IAAA,CAAK,QAAQ,WAAW,CAAA,CAAE,KAAK,IAAI,CAAA;AAC/D,IAAA,MAAM,oBAAqC,EAAC;AAE5C,IAAA,MAAM,aAAa,YAA2B;AAC5C,MAAA,OAAO,KAAK,MAAA,KAAA,WAAA,kBAAmC;AAE7C,QAAA,MAAM,UAAA,GAAa,YAAA,EAAA;AACnB,QAAA,IAAI,UAAA,IAAc,eAAe,MAAA,EAAQ;AACvC,UAAA;AAAA,QACF;AAEA,QAAA,MAAM,KAAA,GAAQ,eAAe,UAAU,CAAA;AAEvC,QAAA,MAAM,aAAA,GAAgB,IAAA,CAAK,WAAA,CAAY,KAAK,CAAA;AAC5C,QAAA,iBAAA,CAAkB,KAAK,aAAa,CAAA;AAGpC,QAAA,MAAM,aAAA;AAAA,MACR;AAAA,IACF,CAAA;AAGA,IAAA,MAAM,mBAAA,GAAsB,SAAA,CAAU,GAAA,CAAI,MAAM,YAAY,CAAA;AAG5D,IAAA,MAAM,OAAA,CAAQ,IAAI,mBAAmB,CAAA;AAGrC,IAAA,MAAM,OAAA,CAAQ,IAAI,iBAAiB,CAAA;AAGnC,IAAA,MAAM,WAAA,GAAc,KAAK,MAAA,CAAO,MAAA;AAChC,IAAA,MAAM,aAAA,GAAgB,KAAK,cAAA,CAAe,IAAA;AAC1C,IAAA,IAAI,gBAAgB,WAAA,EAAa;AAC/B,MAAA,MAAM,gBAAgB,IAAA,CAAK,MAAA,CACxB,IAAI,CAAC,KAAA,EAAO,UAAW,CAAC,IAAA,CAAK,eAAe,GAAA,CAAI,KAAK,IAAI,KAAA,GAAQ,IAAK,EACtE,MAAA,CAAO,CAAA,KAAA,KAAS,UAAU,IAAI,CAAA;AACjC,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,uDAAA,EAAa,aAAa,CAAA,CAAA,EAAI,WAAW,kEAAgB,aAAA,CAAc,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,OACnF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,iBAAA,GAAoD;AAChE,IAAA,IAAI,CAAC,KAAK,MAAA,EAAQ;AAChB,MAAA,OAAO,IAAA;AAAA,IACT;AAEA,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,OAAA;AAAA,QAC1B,CAAA,2BAAA,EAA8B,KAAK,MAAM,CAAA,CAAA;AAAA,QACzC;AAAA,UACE,MAAA,EAAQ,KAAA;AAAA,UACR,OAAA,EAAS,KAAK,OAAA,CAAQ;AAAA;AACxB,OACF;AAEA,MAAA,IAAI,QAAA,CAAS,SAAS,GAAA,EAAK;AACzB,QAAA,OAAO,QAAA,CAAS,IAAA;AAAA,MAClB;AAAA,IACF,SAAS,KAAA,EAAO;AACd,MAAA,OAAA,CAAQ,IAAA,CAAK,qDAAa,KAAK,CAAA;AAAA,IACjC;AAEA,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,cAAA,GAAkD;AAC9D,IAAA,IAAI,CAAC,KAAK,MAAA,EAAQ;AAChB,MAAA,MAAM,IAAI,MAAM,kCAAS,CAAA;AAAA,IAC3B;AAEA,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,OAAA;AAAA,MAC1B,CAAA,2BAAA,EAA8B,KAAK,MAAM,CAAA,CAAA;AAAA,MACzC;AAAA,QACE,MAAA,EAAQ,MAAA;AAAA,QACR,OAAA,EAAS,KAAK,OAAA,CAAQ;AAAA;AACxB,KACF;AAEA,IAAA,IAAI,QAAA,CAAS,SAAS,GAAA,EAAK;AACzB,MAAA,MAAM,IAAI,KAAA,CAAM,QAAA,CAAS,OAAA,IAAW,sCAAQ,CAAA;AAAA,IAC9C;AAEA,IAAA,OAAO,QAAA,CAAS,IAAA;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,cAAA,GAAgC;AAC5C,IAAA,IAAI,CAAC,KAAK,MAAA,EAAQ;AAElB,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,OAAA;AAAA,QAC1B,CAAA,2BAAA,EAA8B,KAAK,MAAM,CAAA,CAAA;AAAA,QACzC;AAAA,UACE,MAAA,EAAQ,KAAA;AAAA,UACR,OAAA,EAAS,KAAK,OAAA,CAAQ;AAAA;AACxB,OACF;AAEA,MAAA,IAAI,QAAA,CAAS,IAAA,KAAS,GAAA,IAAO,IAAA,CAAK,QAAQ,UAAA,EAAY;AACpD,QAAA,IAAA,CAAK,OAAA,CAAQ,UAAA,CAAW,QAAA,CAAS,IAAI,CAAA;AAAA,MACvC;AAAA,IACF,SAAS,KAAA,EAAO;AACd,MAAA,OAAA,CAAQ,IAAA,CAAK,qDAAa,KAAK,CAAA;AAAA,IACjC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAAA,GAA0C;AAC9C,IAAA,IAAI;AACF,MAAA,IAAA,CAAK,MAAA,GAAA,WAAA;AACL,MAAA,IAAA,CAAK,eAAA,GAAkB,IAAI,eAAA,EAAgB;AAG3C,MAAA,MAAM,YAAA,GAAe,MAAM,IAAA,CAAK,UAAA,EAAW;AAC3C,MAAA,IAAA,CAAK,SAAS,YAAA,CAAa,MAAA;AAG3B,MAAA,IAAI,YAAA,CAAa,aAAA,IAAiB,YAAA,CAAa,OAAA,EAAS;AACtD,QAAA,IAAA,CAAK,MAAA,GAAA,WAAA;AACL,QAAA,MAAMC,OAAAA,GAAiC;AAAA,UACrC,QAAQ,IAAA,CAAK,MAAA;AAAA,UACb,SAAS,YAAA,CAAa,OAAA;AAAA,UACtB,QAAA,EAAU,KAAK,IAAA,CAAK,IAAA;AAAA,UACpB,QAAA,EAAU,KAAK,IAAA,CAAK,IAAA;AAAA,UACpB,OAAA,EAAS,EAAA;AAAA,UACT,OAAA,EAAS,IAAA;AAAA,UACT,OAAA,EAAS;AAAA,SACX;AACA,QAAA,IAAA,CAAK,OAAA,CAAQ,WAAWA,OAAM,CAAA;AAC9B,QAAA,OAAOA,OAAAA;AAAA,MACT;AAGA,MAAA,IAAA,CAAK,aAAA,EAAc;AAGnB,MAAA,MAAM,cAAA,GAAiB,MAAM,IAAA,CAAK,iBAAA,EAAkB;AACpD,MAAA,cAAA,CAAe,QAAQ,CAAA,KAAA,KAAS,IAAA,CAAK,cAAA,CAAe,GAAA,CAAI,KAAK,CAAC,CAAA;AAG9D,MAAA,MAAM,KAAK,wBAAA,EAAyB;AAGpC,MAAA,MAAM,WAAA,GAAc,KAAK,MAAA,CAAO,MAAA;AAChC,MAAA,MAAM,aAAA,GAAgB,KAAK,cAAA,CAAe,IAAA;AAC1C,MAAA,IAAI,gBAAgB,WAAA,EAAa;AAC/B,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,CAAA,6DAAA,EAAc,aAAa,CAAA,CAAA,EAAI,WAAW,CAAA,6DAAA;AAAA,SAC5C;AAAA,MACF;AAGA,MAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,iBAAA,EAAkB;AAC9C,MAAA,IAAI,CAAC,QAAA,IAAY,QAAA,CAAS,UAAA,GAAa,GAAA,EAAK;AAC1C,QAAA,MAAM,OAAA,GAAU,QAAA,GAAW,QAAA,CAAS,UAAA,GAAa,CAAA;AACjD,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,6DAAA,EAAc,OAAO,CAAA,CAAA,CAAG,CAAA;AAAA,MAC1C;AAGA,MAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,cAAA,EAAe;AAEzC,MAAA,IAAA,CAAK,MAAA,GAAA,WAAA;AACL,MAAA,IAAA,CAAK,OAAA,CAAQ,WAAW,MAAM,CAAA;AAE9B,MAAA,OAAO,MAAA;AAAA,IACT,SAAS,KAAA,EAAO;AACd,MAAA,IAAA,CAAK,MAAA,GAAA,QAAA;AACL,MAAA,MAAM,GAAA,GAAM,iBAAiB,KAAA,GAAQ,KAAA,GAAQ,IAAI,KAAA,CAAM,MAAA,CAAO,KAAK,CAAC,CAAA;AACpE,MAAA,IAAA,CAAK,OAAA,CAAQ,QAAQ,GAAG,CAAA;AACxB,MAAA,MAAM,GAAA;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,KAAA,GAAc;AACZ,IAAA,IAAI,KAAK,MAAA,KAAA,WAAA,kBAAmC;AAC1C,MAAA,IAAA,CAAK,MAAA,GAAA,QAAA;AACL,MAAA,IAAI,KAAK,eAAA,EAAiB;AACxB,QAAA,IAAA,CAAK,gBAAgB,KAAA,EAAM;AAAA,MAC7B;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAAA,GAA0C;AAC9C,IAAA,IAAI,KAAK,MAAA,KAAA,QAAA,eAAgC;AACvC,MAAA,OAAO,KAAK,MAAA,EAAO;AAAA,IACrB;AACA,IAAA,MAAM,IAAI,MAAM,8DAAY,CAAA;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAAA,GAAwB;AAC5B,IAAA,IAAI,IAAA,CAAK,MAAA,IAAU,IAAA,CAAK,MAAA,KAAA,WAAA,kBAAmC;AACzD,MAAA,IAAI;AACF,QAAA,MAAM,IAAA,CAAK,OAAA,CAA2B,CAAA,yBAAA,EAA4B,IAAA,CAAK,MAAM,CAAA,CAAA,EAAI;AAAA,UAC/E,MAAA,EAAQ,MAAA;AAAA,UACR,OAAA,EAAS,KAAK,OAAA,CAAQ;AAAA,SACvB,CAAA;AAAA,MACH,SAAS,KAAA,EAAO;AACd,QAAA,OAAA,CAAQ,IAAA,CAAK,yCAAW,KAAK,CAAA;AAAA,MAC/B;AAAA,IACF;AAEA,IAAA,IAAA,CAAK,MAAA,GAAA,WAAA;AACL,IAAA,IAAI,KAAK,eAAA,EAAiB;AACxB,MAAA,IAAA,CAAK,gBAAgB,KAAA,EAAM;AAAA,IAC7B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,SAAA,GAA0B;AACxB,IAAA,OAAO,IAAA,CAAK,MAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,SAAA,GAA2B;AACzB,IAAA,OAAO,IAAA,CAAK,MAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,OAAA,CAAW,GAAA,EAAa,OAAA,GAAuB,EAAC,EAAe;AAC3E,IAAA,MAAM,UAAU,CAAA,EAAG,IAAA,CAAK,OAAA,CAAQ,OAAO,GAAG,GAAG,CAAA,CAAA;AAC7C,IAAA,MAAM,MAAA,GAAS,KAAK,eAAA,EAAiB,MAAA;AAErC,IAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,OAAA,EAAS;AAAA,MACpC,GAAG,OAAA;AAAA,MACH;AAAA,KACD,CAAA;AAED,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,MAAM,IAAI,MAAM,CAAA,kBAAA,EAAW,QAAA,CAAS,MAAM,CAAA,CAAA,EAAI,QAAA,CAAS,UAAU,CAAA,CAAE,CAAA;AAAA,IACrE;AAEA,IAAA,OAAO,SAAS,IAAA,EAAK;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKQ,MAAM,EAAA,EAA2B;AACvC,IAAA,OAAO,IAAI,OAAA,CAAQ,CAAA,OAAA,KAAW,UAAA,CAAW,OAAA,EAAS,EAAE,CAAC,CAAA;AAAA,EACvD;AACF;AAQO,SAAS,cAAA,CAAe,MAAY,OAAA,EAAwC;AACjF,EAAA,OAAO,IAAI,aAAA,CAAc,IAAA,EAAM,OAAO,CAAA;AACxC","file":"chunk-RZAVU7BP.mjs","sourcesContent":["/**\n * 文件上传相关类型定义\n */\n\n/**\n * 上传初始化请求参数\n */\nexport interface UploadInitRequest {\n fileName: string;\n fileSize: number;\n fileMd5: string;\n chunkSize?: number;\n}\n\n/**\n * 上传初始化响应\n */\nexport interface UploadInitResponse {\n taskId: string;\n totalChunks: number;\n existingChunks: number[];\n instantUpload: boolean;\n fileUrl?: string;\n chunkSize: number;\n}\n\n/**\n * 分片上传响应\n */\nexport interface ChunkUploadResponse {\n chunkIndex: number;\n success: boolean;\n message: string;\n chunkMd5: string;\n}\n\n/**\n * 完成上传响应\n */\nexport interface CompleteUploadResponse {\n taskId: string;\n fileUrl: string;\n fileName: string;\n fileSize: number;\n fileMd5: string;\n success: boolean;\n message: string;\n}\n\n/**\n * 上传进度信息\n */\nexport interface UploadProgress {\n taskId: string;\n fileName: string;\n fileSize: number;\n totalChunks: number;\n uploadedChunks: number;\n percentage: number;\n status: string;\n uploadedSize: number;\n}\n\n/**\n * API响应包装类型\n */\nexport interface ApiResponse<T> {\n code: number;\n message: string;\n data: T;\n timestamp: number;\n}\n\n/**\n * 上传配置选项\n */\nexport interface UploadOptions {\n /** 分片大小(字节),默认 2MB */\n chunkSize?: number;\n /** 并发上传数量,默认 3 */\n concurrency?: number;\n /** 重试次数,默认 3 */\n retryCount?: number;\n /** 重试延迟(毫秒),默认 1000 */\n retryDelay?: number;\n /** 基础API地址 */\n baseURL?: string;\n /** 请求头 */\n headers?: Record<string, string>;\n /** 上传进度回调 */\n onProgress?: (progress: UploadProgress) => void;\n /** 上传完成回调 */\n onComplete?: (result: CompleteUploadResponse) => void;\n /** 上传错误回调 */\n onError?: (error: Error) => void;\n}\n\n/**\n * 上传状态\n */\nexport enum UploadStatus {\n PENDING = 'pending',\n UPLOADING = 'uploading',\n PAUSED = 'paused',\n COMPLETED = 'completed',\n FAILED = 'failed',\n CANCELLED = 'cancelled',\n}\n\n/**\n * 文件分片信息\n */\nexport interface ChunkInfo {\n index: number;\n start: number;\n end: number;\n blob: Blob;\n md5?: string;\n}\n","/**\n * 文件分片上传工具类\n * 支持分片上传、断点续传、进度追踪\n */\n\nimport {\n UploadOptions,\n UploadInitRequest,\n UploadInitResponse,\n ChunkUploadResponse,\n CompleteUploadResponse,\n UploadProgress,\n UploadStatus,\n ChunkInfo,\n ApiResponse,\n} from '../types/upload';\nimport { calculateBlobMD5, splitFileIntoChunks } from './file';\n\n/**\n * 文件上传器类\n */\nexport class ChunkUploader {\n private file: File;\n private options: Required<UploadOptions>;\n private taskId: string | null = null;\n private chunks: ChunkInfo[] = [];\n private uploadedChunks: Set<number> = new Set();\n private status: UploadStatus = UploadStatus.PENDING;\n private abortController: AbortController | null = null;\n private initResponse: UploadInitResponse | null = null;\n\n constructor(file: File, options: UploadOptions = {}) {\n this.file = file;\n this.options = {\n chunkSize: options.chunkSize || 2 * 1024 * 1024, // 默认2MB\n concurrency: options.concurrency || 3,\n retryCount: options.retryCount || 3,\n retryDelay: options.retryDelay || 1000,\n baseURL: options.baseURL || '',\n headers: options.headers || {},\n onProgress: options.onProgress || (() => {}),\n onComplete: options.onComplete || (() => {}),\n onError: options.onError || (() => {}),\n };\n }\n\n /**\n * 初始化上传\n */\n private async initUpload(): Promise<UploadInitResponse> {\n const fileMd5 = await this.calculateFileMD5();\n const request: UploadInitRequest = {\n fileName: this.file.name,\n fileSize: this.file.size,\n fileMd5,\n chunkSize: this.options.chunkSize,\n };\n\n const response = await this.request<ApiResponse<UploadInitResponse>>('/api/files/common/init', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...this.options.headers,\n },\n body: JSON.stringify(request),\n });\n\n if (response.code !== 200) {\n throw new Error(response.message || '初始化上传失败');\n }\n\n return response.data;\n }\n\n /**\n * 计算文件MD5(简化版,实际应该使用spark-md5等库)\n */\n private async calculateFileMD5(): Promise<string> {\n // 这里使用简化的方法,实际项目中应该使用spark-md5库\n const chunks = splitFileIntoChunks(this.file, this.options.chunkSize);\n const md5Promises = chunks.map((chunk, index) =>\n calculateBlobMD5(chunk).then(md5 => ({ index, md5 }))\n );\n const md5Results = await Promise.all(md5Promises);\n // 简单拼接,实际应该使用正确的MD5算法\n return md5Results.map(r => r.md5).join('');\n }\n\n /**\n * 准备分片\n */\n private prepareChunks(): void {\n const blobChunks = splitFileIntoChunks(this.file, this.options.chunkSize);\n this.chunks = blobChunks.map((blob, index) => ({\n index,\n start: index * this.options.chunkSize,\n end: Math.min((index + 1) * this.options.chunkSize, this.file.size),\n blob,\n }));\n }\n\n /**\n * 获取已上传的分片列表\n */\n private async getUploadedChunks(): Promise<number[]> {\n if (!this.taskId) return [];\n\n try {\n const response = await this.request<ApiResponse<number[]>>(\n `/api/files/common/chunks/${this.taskId}`,\n {\n method: 'GET',\n headers: this.options.headers,\n }\n );\n\n if (response.code === 200) {\n return response.data || [];\n }\n } catch (error) {\n console.warn('获取已上传分片失败:', error);\n }\n\n return [];\n }\n\n /**\n * 上传单个分片\n */\n private async uploadChunk(chunkInfo: ChunkInfo, retryCount = 0): Promise<void> {\n if (!this.taskId) {\n throw new Error('任务ID不存在');\n }\n\n if (this.uploadedChunks.has(chunkInfo.index)) {\n return; // 已上传,跳过\n }\n\n try {\n // 计算分片MD5\n const chunkMd5 = await calculateBlobMD5(chunkInfo.blob);\n\n const formData = new FormData();\n formData.append('file', chunkInfo.blob, this.file.name);\n\n const url = `/api/files/common/chunk?taskId=${this.taskId}&chunkIndex=${chunkInfo.index}&chunkMd5=${chunkMd5}`;\n\n const response = await this.request<ApiResponse<ChunkUploadResponse>>(url, {\n method: 'POST',\n headers: this.options.headers,\n body: formData,\n });\n\n if (response.code === 200 && response.data.success) {\n this.uploadedChunks.add(chunkInfo.index);\n this.updateProgress();\n } else {\n throw new Error(response.message || '分片上传失败');\n }\n } catch (error) {\n if (retryCount < this.options.retryCount) {\n await this.delay(this.options.retryDelay);\n return this.uploadChunk(chunkInfo, retryCount + 1);\n }\n throw error;\n }\n }\n\n /**\n * 并发上传分片\n */\n private async uploadChunksConcurrently(): Promise<void> {\n const chunksToUpload = this.chunks.filter(chunk => !this.uploadedChunks.has(chunk.index));\n\n if (chunksToUpload.length === 0) {\n return; // 所有分片已上传\n }\n\n // 使用信号量控制并发数\n let currentIndex = 0;\n const semaphore = new Array(this.options.concurrency).fill(null);\n const allUploadPromises: Promise<void>[] = [];\n\n const uploadNext = async (): Promise<void> => {\n while (this.status === UploadStatus.UPLOADING) {\n // 原子性地获取下一个分片索引\n const chunkIndex = currentIndex++;\n if (chunkIndex >= chunksToUpload.length) {\n break; // 没有更多分片需要上传\n }\n\n const chunk = chunksToUpload[chunkIndex];\n // 创建上传 Promise 并添加到总数组中\n const uploadPromise = this.uploadChunk(chunk);\n allUploadPromises.push(uploadPromise);\n\n // 等待当前分片上传完成(这样可以控制并发数)\n await uploadPromise;\n }\n };\n\n // 启动并发上传任务(最多 concurrency 个并发)\n const concurrencyPromises = semaphore.map(() => uploadNext());\n\n // 等待所有并发任务完成\n await Promise.all(concurrencyPromises);\n\n // 确保所有上传 Promise 都已完成(双重保险)\n await Promise.all(allUploadPromises);\n\n // 最终验证所有分片都已上传\n const totalChunks = this.chunks.length;\n const uploadedCount = this.uploadedChunks.size;\n if (uploadedCount < totalChunks) {\n const missingChunks = this.chunks\n .map((chunk, index) => (!this.uploadedChunks.has(index) ? index : null))\n .filter(index => index !== null);\n throw new Error(\n `上传未完成:已上传 ${uploadedCount}/${totalChunks} 个分片,缺失分片索引: ${missingChunks.join(', ')}`\n );\n }\n }\n\n /**\n * 获取上传进度(用于验证上传是否完成)\n */\n private async getUploadProgress(): Promise<UploadProgress | null> {\n if (!this.taskId) {\n return null;\n }\n\n try {\n const response = await this.request<ApiResponse<UploadProgress>>(\n `/api/files/common/progress/${this.taskId}`,\n {\n method: 'GET',\n headers: this.options.headers,\n }\n );\n\n if (response.code === 200) {\n return response.data;\n }\n } catch (error) {\n console.warn('获取上传进度失败:', error);\n }\n\n return null;\n }\n\n /**\n * 完成上传(调用后端合并分片接口)\n *\n * 只有在进度为 100% 时才会被调用\n */\n private async completeUpload(): Promise<CompleteUploadResponse> {\n if (!this.taskId) {\n throw new Error('任务ID不存在');\n }\n\n const response = await this.request<ApiResponse<CompleteUploadResponse>>(\n `/api/files/common/complete/${this.taskId}`,\n {\n method: 'POST',\n headers: this.options.headers,\n }\n );\n\n if (response.code !== 200) {\n throw new Error(response.message || '完成上传失败');\n }\n\n return response.data;\n }\n\n /**\n * 更新上传进度(仅用于回调前端显示)\n */\n private async updateProgress(): Promise<void> {\n if (!this.taskId) return;\n\n try {\n const response = await this.request<ApiResponse<UploadProgress>>(\n `/api/files/common/progress/${this.taskId}`,\n {\n method: 'GET',\n headers: this.options.headers,\n }\n );\n\n if (response.code === 200 && this.options.onProgress) {\n this.options.onProgress(response.data);\n }\n } catch (error) {\n console.warn('获取上传进度失败:', error);\n }\n }\n\n /**\n * 开始上传\n */\n async upload(): Promise<CompleteUploadResponse> {\n try {\n this.status = UploadStatus.UPLOADING;\n this.abortController = new AbortController();\n\n // 1. 初始化上传\n const initResponse = await this.initUpload();\n this.taskId = initResponse.taskId;\n\n // 如果已经完成上传(秒传)\n if (initResponse.instantUpload && initResponse.fileUrl) {\n this.status = UploadStatus.COMPLETED;\n const result: CompleteUploadResponse = {\n taskId: this.taskId,\n fileUrl: initResponse.fileUrl,\n fileName: this.file.name,\n fileSize: this.file.size,\n fileMd5: '',\n success: true,\n message: '文件已存在,秒传成功',\n };\n this.options.onComplete(result);\n return result;\n }\n\n // 2. 准备分片\n this.prepareChunks();\n\n // 3. 获取已上传的分片(断点续传)\n const existingChunks = await this.getUploadedChunks();\n existingChunks.forEach(index => this.uploadedChunks.add(index));\n\n // 4. 上传分片\n await this.uploadChunksConcurrently();\n\n // 5. 再次验证所有分片都已上传(双重检查)\n const totalChunks = this.chunks.length;\n const uploadedCount = this.uploadedChunks.size;\n if (uploadedCount < totalChunks) {\n throw new Error(\n `上传验证失败:已上传 ${uploadedCount}/${totalChunks} 个分片,无法完成上传`\n );\n }\n\n // 6. 获取最终上传进度,确认100%完成\n const progress = await this.getUploadProgress();\n if (!progress || progress.percentage < 100) {\n const current = progress ? progress.percentage : 0;\n throw new Error(`上传未完成:当前进度 ${current}%`);\n }\n\n // 7. 进度为 100% 时,调用完成接口,通知后端合并分片\n const result = await this.completeUpload();\n\n this.status = UploadStatus.COMPLETED;\n this.options.onComplete(result);\n\n return result;\n } catch (error) {\n this.status = UploadStatus.FAILED;\n const err = error instanceof Error ? error : new Error(String(error));\n this.options.onError(err);\n throw err;\n }\n }\n\n /**\n * 暂停上传\n */\n pause(): void {\n if (this.status === UploadStatus.UPLOADING) {\n this.status = UploadStatus.PAUSED;\n if (this.abortController) {\n this.abortController.abort();\n }\n }\n }\n\n /**\n * 恢复上传\n */\n async resume(): Promise<CompleteUploadResponse> {\n if (this.status === UploadStatus.PAUSED) {\n return this.upload();\n }\n throw new Error('当前状态无法恢复上传');\n }\n\n /**\n * 取消上传\n */\n async cancel(): Promise<void> {\n if (this.taskId && this.status === UploadStatus.UPLOADING) {\n try {\n await this.request<ApiResponse<null>>(`/api/files/common/cancel/${this.taskId}`, {\n method: 'POST',\n headers: this.options.headers,\n });\n } catch (error) {\n console.warn('取消上传失败:', error);\n }\n }\n\n this.status = UploadStatus.CANCELLED;\n if (this.abortController) {\n this.abortController.abort();\n }\n }\n\n /**\n * 获取当前状态\n */\n getStatus(): UploadStatus {\n return this.status;\n }\n\n /**\n * 获取任务ID\n */\n getTaskId(): string | null {\n return this.taskId;\n }\n\n /**\n * HTTP请求封装\n */\n private async request<T>(url: string, options: RequestInit = {}): Promise<T> {\n const fullUrl = `${this.options.baseURL}${url}`;\n const signal = this.abortController?.signal;\n\n const response = await fetch(fullUrl, {\n ...options,\n signal,\n });\n\n if (!response.ok) {\n throw new Error(`HTTP错误: ${response.status} ${response.statusText}`);\n }\n\n return response.json();\n }\n\n /**\n * 延迟函数\n */\n private delay(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n}\n\n/**\n * 创建文件上传器实例\n * @param file - 文件对象\n * @param options - 上传配置选项\n * @returns 上传器实例\n */\nexport function createUploader(file: File, options?: UploadOptions): ChunkUploader {\n return new ChunkUploader(file, options);\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/types/upload.ts","../src/browser/upload.ts"],"names":["UploadStatus","splitFileIntoChunks","calculateBlobMD5","result"],"mappings":"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","file":"chunk-TADFN7AL.js","sourcesContent":["/**\n * 文件上传相关类型定义\n */\n\n/**\n * 上传初始化请求参数\n */\nexport interface UploadInitRequest {\n fileName: string;\n fileSize: number;\n fileMd5: string;\n chunkSize?: number;\n}\n\n/**\n * 上传初始化响应\n */\nexport interface UploadInitResponse {\n taskId: string;\n totalChunks: number;\n existingChunks: number[];\n instantUpload: boolean;\n fileUrl?: string;\n chunkSize: number;\n}\n\n/**\n * 分片上传响应\n */\nexport interface ChunkUploadResponse {\n chunkIndex: number;\n success: boolean;\n message: string;\n chunkMd5: string;\n}\n\n/**\n * 完成上传响应\n */\nexport interface CompleteUploadResponse {\n taskId: string;\n fileUrl: string;\n fileName: string;\n fileSize: number;\n fileMd5: string;\n success: boolean;\n message: string;\n}\n\n/**\n * 上传进度信息\n */\nexport interface UploadProgress {\n taskId: string;\n fileName: string;\n fileSize: number;\n totalChunks: number;\n uploadedChunks: number;\n percentage: number;\n status: string;\n uploadedSize: number;\n}\n\n/**\n * API响应包装类型\n */\nexport interface ApiResponse<T> {\n code: number;\n message: string;\n data: T;\n timestamp: number;\n}\n\n/**\n * 上传配置选项\n */\nexport interface UploadOptions {\n /** 分片大小(字节),默认 2MB */\n chunkSize?: number;\n /** 并发上传数量,默认 3 */\n concurrency?: number;\n /** 重试次数,默认 3 */\n retryCount?: number;\n /** 重试延迟(毫秒),默认 1000 */\n retryDelay?: number;\n /** 基础API地址 */\n baseURL?: string;\n /** 请求头 */\n headers?: Record<string, string>;\n /** 上传进度回调 */\n onProgress?: (progress: UploadProgress) => void;\n /** 上传完成回调 */\n onComplete?: (result: CompleteUploadResponse) => void;\n /** 上传错误回调 */\n onError?: (error: Error) => void;\n}\n\n/**\n * 上传状态\n */\nexport enum UploadStatus {\n PENDING = 'pending',\n UPLOADING = 'uploading',\n PAUSED = 'paused',\n COMPLETED = 'completed',\n FAILED = 'failed',\n CANCELLED = 'cancelled',\n}\n\n/**\n * 文件分片信息\n */\nexport interface ChunkInfo {\n index: number;\n start: number;\n end: number;\n blob: Blob;\n md5?: string;\n}\n","/**\n * 文件分片上传工具类\n * 支持分片上传、断点续传、进度追踪\n */\n\nimport {\n UploadOptions,\n UploadInitRequest,\n UploadInitResponse,\n ChunkUploadResponse,\n CompleteUploadResponse,\n UploadProgress,\n UploadStatus,\n ChunkInfo,\n ApiResponse,\n} from '../types/upload';\nimport { calculateBlobMD5, splitFileIntoChunks } from './file';\n\n/**\n * 文件上传器类\n */\nexport class ChunkUploader {\n private file: File;\n private options: Required<UploadOptions>;\n private taskId: string | null = null;\n private chunks: ChunkInfo[] = [];\n private uploadedChunks: Set<number> = new Set();\n private status: UploadStatus = UploadStatus.PENDING;\n private abortController: AbortController | null = null;\n private initResponse: UploadInitResponse | null = null;\n\n constructor(file: File, options: UploadOptions = {}) {\n this.file = file;\n this.options = {\n chunkSize: options.chunkSize || 2 * 1024 * 1024, // 默认2MB\n concurrency: options.concurrency || 3,\n retryCount: options.retryCount || 3,\n retryDelay: options.retryDelay || 1000,\n baseURL: options.baseURL || '',\n headers: options.headers || {},\n onProgress: options.onProgress || (() => {}),\n onComplete: options.onComplete || (() => {}),\n onError: options.onError || (() => {}),\n };\n }\n\n /**\n * 初始化上传\n */\n private async initUpload(): Promise<UploadInitResponse> {\n const fileMd5 = await this.calculateFileMD5();\n const request: UploadInitRequest = {\n fileName: this.file.name,\n fileSize: this.file.size,\n fileMd5,\n chunkSize: this.options.chunkSize,\n };\n\n const response = await this.request<ApiResponse<UploadInitResponse>>('/api/files/common/init', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...this.options.headers,\n },\n body: JSON.stringify(request),\n });\n\n if (response.code !== 200) {\n throw new Error(response.message || '初始化上传失败');\n }\n\n return response.data;\n }\n\n /**\n * 计算文件MD5(简化版,实际应该使用spark-md5等库)\n */\n private async calculateFileMD5(): Promise<string> {\n // 这里使用简化的方法,实际项目中应该使用spark-md5库\n const chunks = splitFileIntoChunks(this.file, this.options.chunkSize);\n const md5Promises = chunks.map((chunk, index) =>\n calculateBlobMD5(chunk).then(md5 => ({ index, md5 }))\n );\n const md5Results = await Promise.all(md5Promises);\n // 简单拼接,实际应该使用正确的MD5算法\n return md5Results.map(r => r.md5).join('');\n }\n\n /**\n * 准备分片\n */\n private prepareChunks(): void {\n const blobChunks = splitFileIntoChunks(this.file, this.options.chunkSize);\n this.chunks = blobChunks.map((blob, index) => ({\n index,\n start: index * this.options.chunkSize,\n end: Math.min((index + 1) * this.options.chunkSize, this.file.size),\n blob,\n }));\n }\n\n /**\n * 获取已上传的分片列表\n */\n private async getUploadedChunks(): Promise<number[]> {\n if (!this.taskId) return [];\n\n try {\n const response = await this.request<ApiResponse<number[]>>(\n `/api/files/common/chunks/${this.taskId}`,\n {\n method: 'GET',\n headers: this.options.headers,\n }\n );\n\n if (response.code === 200) {\n return response.data || [];\n }\n } catch (error) {\n console.warn('获取已上传分片失败:', error);\n }\n\n return [];\n }\n\n /**\n * 上传单个分片\n */\n private async uploadChunk(chunkInfo: ChunkInfo, retryCount = 0): Promise<void> {\n if (!this.taskId) {\n throw new Error('任务ID不存在');\n }\n\n if (this.uploadedChunks.has(chunkInfo.index)) {\n return; // 已上传,跳过\n }\n\n try {\n // 计算分片MD5\n const chunkMd5 = await calculateBlobMD5(chunkInfo.blob);\n\n const formData = new FormData();\n formData.append('file', chunkInfo.blob, this.file.name);\n\n const url = `/api/files/common/chunk?taskId=${this.taskId}&chunkIndex=${chunkInfo.index}&chunkMd5=${chunkMd5}`;\n\n const response = await this.request<ApiResponse<ChunkUploadResponse>>(url, {\n method: 'POST',\n headers: this.options.headers,\n body: formData,\n });\n\n if (response.code === 200 && response.data.success) {\n this.uploadedChunks.add(chunkInfo.index);\n this.updateProgress();\n } else {\n throw new Error(response.message || '分片上传失败');\n }\n } catch (error) {\n if (retryCount < this.options.retryCount) {\n await this.delay(this.options.retryDelay);\n return this.uploadChunk(chunkInfo, retryCount + 1);\n }\n throw error;\n }\n }\n\n /**\n * 并发上传分片\n */\n private async uploadChunksConcurrently(): Promise<void> {\n const chunksToUpload = this.chunks.filter(chunk => !this.uploadedChunks.has(chunk.index));\n\n if (chunksToUpload.length === 0) {\n return; // 所有分片已上传\n }\n\n // 使用信号量控制并发数\n let currentIndex = 0;\n const semaphore = new Array(this.options.concurrency).fill(null);\n const allUploadPromises: Promise<void>[] = [];\n\n const uploadNext = async (): Promise<void> => {\n while (this.status === UploadStatus.UPLOADING) {\n // 原子性地获取下一个分片索引\n const chunkIndex = currentIndex++;\n if (chunkIndex >= chunksToUpload.length) {\n break; // 没有更多分片需要上传\n }\n\n const chunk = chunksToUpload[chunkIndex];\n // 创建上传 Promise 并添加到总数组中\n const uploadPromise = this.uploadChunk(chunk);\n allUploadPromises.push(uploadPromise);\n\n // 等待当前分片上传完成(这样可以控制并发数)\n await uploadPromise;\n }\n };\n\n // 启动并发上传任务(最多 concurrency 个并发)\n const concurrencyPromises = semaphore.map(() => uploadNext());\n\n // 等待所有并发任务完成\n await Promise.all(concurrencyPromises);\n\n // 确保所有上传 Promise 都已完成(双重保险)\n await Promise.all(allUploadPromises);\n\n // 最终验证所有分片都已上传\n const totalChunks = this.chunks.length;\n const uploadedCount = this.uploadedChunks.size;\n if (uploadedCount < totalChunks) {\n const missingChunks = this.chunks\n .map((chunk, index) => (!this.uploadedChunks.has(index) ? index : null))\n .filter(index => index !== null);\n throw new Error(\n `上传未完成:已上传 ${uploadedCount}/${totalChunks} 个分片,缺失分片索引: ${missingChunks.join(', ')}`\n );\n }\n }\n\n /**\n * 获取上传进度(用于验证上传是否完成)\n */\n private async getUploadProgress(): Promise<UploadProgress | null> {\n if (!this.taskId) {\n return null;\n }\n\n try {\n const response = await this.request<ApiResponse<UploadProgress>>(\n `/api/files/common/progress/${this.taskId}`,\n {\n method: 'GET',\n headers: this.options.headers,\n }\n );\n\n if (response.code === 200) {\n return response.data;\n }\n } catch (error) {\n console.warn('获取上传进度失败:', error);\n }\n\n return null;\n }\n\n /**\n * 完成上传(调用后端合并分片接口)\n *\n * 只有在进度为 100% 时才会被调用\n */\n private async completeUpload(): Promise<CompleteUploadResponse> {\n if (!this.taskId) {\n throw new Error('任务ID不存在');\n }\n\n const response = await this.request<ApiResponse<CompleteUploadResponse>>(\n `/api/files/common/complete/${this.taskId}`,\n {\n method: 'POST',\n headers: this.options.headers,\n }\n );\n\n if (response.code !== 200) {\n throw new Error(response.message || '完成上传失败');\n }\n\n return response.data;\n }\n\n /**\n * 更新上传进度(仅用于回调前端显示)\n */\n private async updateProgress(): Promise<void> {\n if (!this.taskId) return;\n\n try {\n const response = await this.request<ApiResponse<UploadProgress>>(\n `/api/files/common/progress/${this.taskId}`,\n {\n method: 'GET',\n headers: this.options.headers,\n }\n );\n\n if (response.code === 200 && this.options.onProgress) {\n this.options.onProgress(response.data);\n }\n } catch (error) {\n console.warn('获取上传进度失败:', error);\n }\n }\n\n /**\n * 开始上传\n */\n async upload(): Promise<CompleteUploadResponse> {\n try {\n this.status = UploadStatus.UPLOADING;\n this.abortController = new AbortController();\n\n // 1. 初始化上传\n const initResponse = await this.initUpload();\n this.taskId = initResponse.taskId;\n\n // 如果已经完成上传(秒传)\n if (initResponse.instantUpload && initResponse.fileUrl) {\n this.status = UploadStatus.COMPLETED;\n const result: CompleteUploadResponse = {\n taskId: this.taskId,\n fileUrl: initResponse.fileUrl,\n fileName: this.file.name,\n fileSize: this.file.size,\n fileMd5: '',\n success: true,\n message: '文件已存在,秒传成功',\n };\n this.options.onComplete(result);\n return result;\n }\n\n // 2. 准备分片\n this.prepareChunks();\n\n // 3. 获取已上传的分片(断点续传)\n const existingChunks = await this.getUploadedChunks();\n existingChunks.forEach(index => this.uploadedChunks.add(index));\n\n // 4. 上传分片\n await this.uploadChunksConcurrently();\n\n // 5. 再次验证所有分片都已上传(双重检查)\n const totalChunks = this.chunks.length;\n const uploadedCount = this.uploadedChunks.size;\n if (uploadedCount < totalChunks) {\n throw new Error(\n `上传验证失败:已上传 ${uploadedCount}/${totalChunks} 个分片,无法完成上传`\n );\n }\n\n // 6. 获取最终上传进度,确认100%完成\n const progress = await this.getUploadProgress();\n if (!progress || progress.percentage < 100) {\n const current = progress ? progress.percentage : 0;\n throw new Error(`上传未完成:当前进度 ${current}%`);\n }\n\n // 7. 进度为 100% 时,调用完成接口,通知后端合并分片\n const result = await this.completeUpload();\n\n this.status = UploadStatus.COMPLETED;\n this.options.onComplete(result);\n\n return result;\n } catch (error) {\n this.status = UploadStatus.FAILED;\n const err = error instanceof Error ? error : new Error(String(error));\n this.options.onError(err);\n throw err;\n }\n }\n\n /**\n * 暂停上传\n */\n pause(): void {\n if (this.status === UploadStatus.UPLOADING) {\n this.status = UploadStatus.PAUSED;\n if (this.abortController) {\n this.abortController.abort();\n }\n }\n }\n\n /**\n * 恢复上传\n */\n async resume(): Promise<CompleteUploadResponse> {\n if (this.status === UploadStatus.PAUSED) {\n return this.upload();\n }\n throw new Error('当前状态无法恢复上传');\n }\n\n /**\n * 取消上传\n */\n async cancel(): Promise<void> {\n if (this.taskId && this.status === UploadStatus.UPLOADING) {\n try {\n await this.request<ApiResponse<null>>(`/api/files/common/cancel/${this.taskId}`, {\n method: 'POST',\n headers: this.options.headers,\n });\n } catch (error) {\n console.warn('取消上传失败:', error);\n }\n }\n\n this.status = UploadStatus.CANCELLED;\n if (this.abortController) {\n this.abortController.abort();\n }\n }\n\n /**\n * 获取当前状态\n */\n getStatus(): UploadStatus {\n return this.status;\n }\n\n /**\n * 获取任务ID\n */\n getTaskId(): string | null {\n return this.taskId;\n }\n\n /**\n * HTTP请求封装\n */\n private async request<T>(url: string, options: RequestInit = {}): Promise<T> {\n const fullUrl = `${this.options.baseURL}${url}`;\n const signal = this.abortController?.signal;\n\n const response = await fetch(fullUrl, {\n ...options,\n signal,\n });\n\n if (!response.ok) {\n throw new Error(`HTTP错误: ${response.status} ${response.statusText}`);\n }\n\n return response.json();\n }\n\n /**\n * 延迟函数\n */\n private delay(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n}\n\n/**\n * 创建文件上传器实例\n * @param file - 文件对象\n * @param options - 上传配置选项\n * @returns 上传器实例\n */\nexport function createUploader(file: File, options?: UploadOptions): ChunkUploader {\n return new ChunkUploader(file, options);\n}\n"]}