# UploadSection **Repository Path**: yanwuhc/upload-section ## Basic Information - **Project Name**: UploadSection - **Description**: 企业级文件上传方案,支持分片上传跟断点续传。 - **Primary Language**: JavaScript - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-03-03 - **Last Updated**: 2026-03-04 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 企业级文件上传系统 - 学习指南 ## 项目概述 这是一个完整的企业级文件上传系统,支持大文件分片上传、动态并发控制和断点续传功能。 ## 核心功能模块 ### 1. 分片上传 (Chunk Upload) **文件位置**: - 前端: `client/src/utils/fileHelper.js` - 后端: `server/routes/upload.js` **核心原理**: - 将大文件切分成多个小片段(默认5MB/片) - 每个分片独立上传,失败可单独重试 - 所有分片上传完成后,在服务器端按顺序合并 **关键代码**: ```javascript // 前端分片切割 export function createFileChunks(file, chunkSize = CHUNK_SIZE) { const chunks = []; const totalChunks = Math.ceil(file.size / chunkSize); for (let i = 0; i < totalChunks; i++) { const start = i * chunkSize; const end = Math.min(start + chunkSize, file.size); const chunk = file.slice(start, end); chunks.push({ index: i, chunk, size: chunk.size, start, end }); } return chunks; } ``` ### 2. 动态并发控制 (Dynamic Concurrency Control) **文件位置**: `client/src/utils/concurrencyControl.js` **核心原理**: - 根据网络状况动态调整并发上传的分片数量 - 监控上传速度、网络延迟、错误率 - 每5秒评估一次,自动调整并发数(1-6) **调整策略**: - 错误率 > 30% → 大幅降低并发数(-2) - 延迟 > 2秒 → 降低并发数(-1) - 速度 > 1MB/s → 增加并发数(+1) - 速度 < 100KB/s → 降低并发数(-1) **关键代码**: ```javascript evaluateAndAdjust() { const avgSpeed = calculateAverageSpeed(); const avgLatency = calculateAverageLatency(); const errorRate = calculateErrorRate(); if (errorRate > this.errorThreshold) { this.adjustConcurrency(-2); } else if (avgLatency > this.latencyThreshold) { this.adjustConcurrency(-1); } else if (avgSpeed > this.speedThresholds.fast) { this.adjustConcurrency(1); } else if (avgSpeed < this.speedThresholds.slow) { this.adjustConcurrency(-1); } } ``` ### 3. 断点续传 (Resume Upload) **文件位置**: - 前端: `client/src/utils/resumeManager.js` - 后端: `server/routes/upload.js` (check接口) **核心原理**: - 使用localStorage存储上传状态(文件hash、已上传分片) - 上传前检查服务器端已上传的分片 - 只上传未完成的分片 - 支持暂停、继续、取消操作 **状态管理**: ```javascript // 保存上传状态 saveUploadState(fileHash, state) { const allStates = this.getAllStates(); allStates[fileHash] = { ...state, timestamp: Date.now() }; localStorage.setItem('upload_resume_state', JSON.stringify(allStates)); } // 检查断点续传状态 async checkResumeState() { const state = this.resumeManager.getUploadState(this.fileHash); if (state) { const response = await uploadAPI.checkUploadStatus(this.fileHash); if (response.success) { const uploadedChunks = response.data.uploadedChunks; this.uploadedChunks = uploadedChunks.length; this.uploadedBytes = uploadedChunks.length * CHUNK_SIZE; this.uploadProgress = Math.round((this.uploadedBytes / this.selectedFile.size) * 100); } } } ``` ### 4. 文件哈希计算 (File Hash Calculation) **文件位置**: `client/src/utils/fileHelper.js` **核心原理**: - 使用SparkMD5库计算文件的MD5哈希值 - 分片计算,避免大文件内存溢出 - 用于文件唯一标识和完整性校验 **关键代码**: ```javascript async function calculateFileHash(file) { const spark = new SparkMD5.ArrayBuffer(); const fileReader = new FileReader(); const chunks = Math.ceil(file.size / CHUNK_SIZE); let currentChunk = 0; fileReader.onload = (e) => { spark.append(e.target.result); currentChunk++; if (currentChunk < chunks) { loadNext(); } else { resolve(spark.end()); } }; function loadNext() { const start = currentChunk * CHUNK_SIZE; const end = Math.min(start + CHUNK_SIZE, file.size); fileReader.readAsArrayBuffer(file.slice(start, end)); } loadNext(); } ``` ### 5. 并发上传优化 (Concurrent Upload Optimization) **文件位置**: `client/src/components/FileUploader.vue` **核心原理**: - 使用currentIndex计数器避免竞态条件 - 多个worker并发上传不同分片 - 失败时重置索引,重新上传该分片 **关键代码**: ```javascript async uploadChunks() { const pendingChunks = this.chunks.filter( chunk => !this.isChunkUploaded(chunk.index) ); let currentIndex = 0; let completedChunks = 0; const totalPending = pendingChunks.length; const uploadNextChunk = async () => { if (this.isPaused || !this.isUploading) return; if (currentIndex >= pendingChunks.length) { if (completedChunks >= totalPending) { await this.mergeChunks(); } return; } const chunkIndex = currentIndex++; const chunk = pendingChunks[chunkIndex]; try { await uploadAPI.uploadChunk(formData, null); completedChunks++; this.currentConcurrency = this.concurrencyController.getConcurrency(); } catch (error) { if (this.isUploading) { await new Promise(resolve => setTimeout(resolve, 1000)); currentIndex = chunkIndex; // 重新上传 } } if (this.isUploading && !this.isPaused) { await uploadNextChunk(); } }; const workers = []; for (let i = 0; i < this.currentConcurrency; i++) { workers.push(uploadNextChunk()); } await Promise.all(workers); } ``` ## 后端API接口 ### 1. 初始化上传 ``` POST /api/upload/init 参数: { fileName, fileSize, fileHash } 返回: { success, data: { fileHash, uploadedChunks } } ``` ### 2. 上传分片 ``` POST /api/upload/chunk 参数: FormData { chunk, hash, chunkIndex } 返回: { success, data: { chunkIndex, hash } } ``` ### 3. 合并分片 ``` POST /api/upload/merge 参数: { fileName, fileSize, fileHash, chunkSize } 返回: { success, data: { url, fileName, fileSize, hash } } ``` ### 4. 检查上传状态 ``` GET /api/upload/check?hash={fileHash} 返回: { success, data: { uploadedChunks } } ``` ### 5. 获取上传进度 ``` GET /api/upload/progress?hash={fileHash} 返回: { success, data: { uploadedChunks, count } } ``` ## 关键技术点 ### 1. 文件分片 - 使用`file.slice(start, end)`切割文件 - 默认分片大小:5MB - 可根据网络状况调整 ### 2. 并发控制 - 初始并发数:3 - 最小并发数:1 - 最大并发数:6 - 动态调整策略基于网络状况 ### 3. 断点续传 - 前端使用localStorage存储状态 - 后端检查已上传分片 - 支持暂停/继续/取消 ### 4. 文件完整性校验 - 使用MD5哈希值校验 - 合并后重新计算哈希 - 确保文件无损坏 ### 5. 错误处理 - 分片上传失败自动重试 - 网络错误延迟1秒后重试 - 最多重试3次 ## 项目结构 ``` upload-section/ ├── client/ # 前端Vue2项目 │ ├── src/ │ │ ├── components/ # Vue组件 │ │ │ ├── FileUploader.vue # 文件上传主组件 │ │ │ ├── ProgressBar.vue # 进度条组件 │ │ │ └── UploadControl.vue # 上传控制组件 │ │ ├── utils/ # 工具类 │ │ │ ├── fileHelper.js # 文件处理工具 │ │ │ ├── concurrencyControl.js # 并发控制 │ │ │ ├── resumeManager.js # 断点续传管理 │ │ │ └── api.js # API接口封装 │ │ ├── App.vue # 根组件 │ │ └── main.js # 入口文件 │ ├── public/ │ │ └── index.html # HTML模板 │ ├── package.json │ └── webpack.config.js # Webpack配置 ├── server/ # 后端Express项目 │ ├── routes/ │ │ └── upload.js # 上传路由 │ ├── uploads/ # 临时分片存储 │ ├── files/ # 最终文件存储 │ ├── app.js # Express应用入口 │ └── package.json └── 技术文档.md # 详细技术文档 ``` ## 使用说明 ### 启动后端服务 ```bash cd server npm install npm start ``` 后端服务将在 `http://localhost:3001` 启动 ### 启动前端服务 ```bash cd client npm install npm run dev ``` 前端服务将在 `http://localhost:8080` 启动 ### 使用流程 1. 打开浏览器访问 `http://localhost:8080` 2. 点击上传区域或拖拽文件到上传区域 3. 系统自动计算文件哈希 4. 点击"开始上传"按钮 5. 可随时暂停、继续或取消上传 6. 上传完成后可下载文件 ## 学习要点 ### 1. 分片上传的优势 - 提高上传成功率 - 支持断点续传 - 便于实现并发上传 - 单个分片失败只需重传该分片 ### 2. 动态并发控制的重要性 - 避免网络拥塞 - 提高上传效率 - 适应不同网络环境 - 提升用户体验 ### 3. 断点续传的实现 - 前端状态管理(localStorage) - 后端状态查询(check接口) - 状态同步机制 - 支持暂停/继续/取消 ### 4. 文件完整性校验 - 使用MD5哈希值 - 合并后重新计算 - 确保文件无损坏 - 防止文件被篡改 ### 5. 错误处理策略 - 自动重试机制 - 延迟重试策略 - 错误日志记录 - 用户友好提示 ## 扩展建议 ### 1. 云存储集成 - 集成阿里云OSS、腾讯云COS等 - 支持分片直接上传到云存储 - 提高上传速度和可靠性 ### 2. 数据库存储 - 使用数据库存储上传记录 - 支持上传历史查询 - 实现文件管理功能 ### 3. 用户认证 - 添加用户登录功能 - 支持个人文件管理 - 实现权限控制 ### 4. 文件预览 - 支持图片、视频等文件预览 - 提供更好的用户体验 - 支持在线编辑功能 ### 5. 上传队列 - 支持多文件批量上传 - 提供上传队列管理 - 实现批量操作功能 ## 总结 本项目实现了一个完整的企业级文件上传系统,包含以下核心功能: 1. ✅ 大文件分片上传 2. ✅ 动态并发控制 3. ✅ 断点续传 4. ✅ 文件哈希计算和完整性校验 5. ✅ 上传进度实时显示 6. ✅ 暂停/继续/取消功能 项目具有良好的扩展性和可维护性,可根据实际需求进行定制和优化。 ## 注意事项 1. **并发上传的竞态条件**: 使用currentIndex计数器避免多个worker同时访问同一个分片 2. **文件哈希计算**: 大文件需要分片计算,避免内存溢出 3. **断点续传状态**: 需要前后端状态同步,确保一致性 4. **错误处理**: 需要完善的错误处理和重试机制 5. **网络状况**: 动态并发控制需要根据实际网络状况调整参数 ## 常见问题 ### Q1: 为什么需要分片上传? A: 分片上传可以提高上传成功率,支持断点续传,便于实现并发上传,单个分片失败只需重传该分片。 ### Q2: 如何调整并发数? A: 系统会根据网络状况自动调整并发数,也可以在代码中修改初始并发数和最大并发数。 ### Q3: 断点续传如何实现? A: 前端使用localStorage存储上传状态,后端提供check接口查询已上传分片,上传前检查状态,只上传未完成的分片。 ### Q4: 如何确保文件完整性? A: 使用MD5哈希值进行校验,合并后重新计算哈希,与原始哈希对比,确保文件无损坏。 ### Q5: 如何优化上传速度? A: 可以根据网络状况调整分片大小、并发数,使用CDN加速,选择合适的云存储服务。 ## 参考资料 - [SparkMD5](https://github.com/satazor/js-spark-md5) - JavaScript MD5实现 - [Multer](https://github.com/expressjs/multer) - Express文件上传中间件 - [Axios](https://github.com/axios/axios) - HTTP客户端 - [Vue.js](https://vuejs.org/) - JavaScript框架 - [Express](https://expressjs.com/) - Node.js Web框架 ## 许可证 MIT License