From 16475f2172f2243d935d8603bb02aae48661e57c Mon Sep 17 00:00:00 2001 From: yangshangbo <42314632+yangshangbo@users.noreply.github.com> Date: Thu, 10 Jul 2025 10:08:59 +0800 Subject: [PATCH] doc: README.MD add chunk file upload instructions --- README.MD | 392 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 392 insertions(+) diff --git a/README.MD b/README.MD index 7c3929e..4e2d5a7 100644 --- a/README.MD +++ b/README.MD @@ -16,6 +16,11 @@ - 🛡️ 内置安全防护(路径遍历攻击防护、点文件访问控制) - 💾 智能缓存支持(ETag、Last-Modified、Cache-Control) - 🎯 自动 Content-Type 检测 +- 📤 **大文件分片上传**:支持大文件分片上传、断点续传、秒传、自动合并 +- 🔄 **断点续传**:支持上传中断后继续上传 +- ⚡ **秒传功能**:基于文件哈希的秒传检测 +- 🗂️ **文件去重**:基于文件哈希的去重机制 +- 💾 **数据库存储**:使用 SQLite 存储文件元数据 ## 安装 @@ -359,6 +364,393 @@ v-hono/ └── sample.json # JSON文件 ``` +## 大文件分片上传系统 + +### 概述 + +V-Hono 大文件分片上传系统是一个基于 V 语言和 Hono 框架构建的高性能文件上传解决方案。支持大文件分片上传、断点续传、秒传、自动合并等功能。 + +### 主要特性 + +- ✅ **分片上传**:支持大文件分片上传,默认分片大小 1MB +- ✅ **断点续传**:支持上传中断后继续上传 +- ✅ **秒传功能**:基于文件哈希的秒传检测 +- ✅ **自动合并**:所有分片上传完成后自动合并文件 +- ✅ **文件去重**:基于文件哈希的去重机制 +- ✅ **数据库存储**:使用 SQLite 存储文件元数据 +- ✅ **RESTful API**:完整的 REST API 接口 +- ✅ **Web 界面**:提供友好的 Web 上传界面 + +### 系统架构 + +``` +前端 (Web/移动端) + ↓ +V-Hono 服务器 + ↓ +分片存储 (./uploads/chunks/) + ↓ +文件合并 (./uploads/files/) + ↓ +数据库 (SQLite) +``` + +### 安装和运行 + +#### 1. 环境要求 + +- V 语言 0.4.x 或更高版本 +- Windows/Linux/macOS + +#### 2. 启动服务器 + +```bash +# 克隆项目 +git clone +cd v-hono + +# 启动分片上传服务器 +v run chunk_upload_example.v +``` + +服务器将在 `http://localhost:8080` 启动。 + +#### 3. 访问 Web 界面 + +打开浏览器访问 `http://localhost:8080` 即可使用 Web 上传界面。 + +### API 接口文档 + +#### 1. 分片上传接口 + +**接口地址:** `POST /upload/chunk` + +**请求参数:** +- `file_hash` (string): 文件哈希值 +- `chunk_index` (int): 分片索引,从 0 开始 +- `filename` (string): 原始文件名 +- `file_size` (int): 文件总大小(字节) +- `chunk_size` (int): 分片大小(字节) +- `chunk_hash` (string): 分片哈希值 +- `chunk` (file): 分片文件数据 + +**响应格式:** + +上传完成(自动合并): +```json +{ + "success": true, + "all_chunk_uploaded": true, + "file_path": ".\\uploads\\files\\xxx.zip", + "file_uuid": "xxx-xxx-xxx", + "message": "File merged successfully" +} +``` + +上传中: +```json +{ + "success": true, + "chunk_index": 1, + "all_chunk_uploaded": false, + "message": "Chunk uploaded successfully" +} +``` + +#### 2. 分片存在检查接口 + +**接口地址:** `GET /upload/chunk_exists` + +**请求参数:** +- `file_hash` (string): 文件哈希值 +- `chunk_index` (int): 分片索引 +- `chunk_hash` (string): 分片哈希值 +- `file_size` (int): 文件总大小 +- `trunk_size` (int): 分片大小 + +**响应格式:** +```json +{ + "exists": true, + "all_chunk_uploaded": false +} +``` + +#### 3. 上传状态查询接口 + +**接口地址:** `GET /upload/status` + +**请求参数:** +- `file_hash` (string): 文件哈希值 + +**响应格式:** +```json +{ + "file_hash": "xxx", + "filename": "example.zip", + "total_chunks": 0, + "file_size": 1048576, + "chunk_size": 2097152, + "uploaded_chunks": [0, 1, 2], + "status": "uploading", + "created_at": 1640995200, + "updated_at": 1640995300 +} +``` + +#### 4. 已上传分片查询接口 + +**接口地址:** `GET /upload/chunks` + +**请求参数:** +- `file_hash` (string): 文件哈希值 + +**响应格式:** +```json +{ + "uploaded_chunks": [0, 1, 2], + "total_chunks": 5, + "completed": false +} +``` + +#### 5. 文件管理接口 + +##### 获取所有文件 +**接口地址:** `GET /api/files` + +##### 根据 UUID 获取文件 +**接口地址:** `GET /api/files/{uuid}` + +##### 根据哈希获取文件 +**接口地址:** `GET /api/files/hash/{hash}` + +##### 删除文件 +**接口地址:** `DELETE /api/files/{uuid}` + +### 配置说明 + +#### ChunkUploadConfig 配置项 + +```v +pub struct ChunkUploadConfig { + chunk_size: int = 1024 * 1024 // 默认分片大小 1MB + max_file_size: int = 1024 * 1024 * 1024 // 最大文件大小 1GB + temp_dir: string = './uploads/chunks' // 临时分片目录 + upload_dir: string = './uploads/files' // 最终文件目录 + cleanup_delay: int = 3600 // 清理延迟时间(秒) + clear_chunks_on_complete: bool = false // 完成后是否清理分片 + db_path: string = './uploads/files.db' // 数据库文件路径 +} +``` + +### 文件存储结构 + +``` +uploads/ +├── chunks/ # 分片文件目录 +│ └── {file_hash}/ # 按文件哈希分组 +│ └── {chunk_size}/ # 按分片大小分组 +│ ├── chunk_0.part # 分片文件 +│ ├── chunk_1.part +│ └── ... +├── files/ # 最终文件目录 +│ ├── {file_hash}.{ext} # 合并后的文件 +│ └── ... +└── files.db # SQLite 数据库文件 +``` + +### 使用示例 + +#### 1. 前端 JavaScript 示例 + +```javascript +// 计算文件哈希 +async function calculateFileHash(file) { + return new Promise(resolve => { + const spark = new SparkMD5.ArrayBuffer(); + const reader = new FileReader(); + reader.onload = e => { + spark.append(e.target.result); + resolve(spark.end()); + }; + reader.readAsArrayBuffer(file); + }); +} + +// 上传分片 +async function uploadChunk(file, chunk, { fileHash, chunkIndex, chunkHash }) { + const form = new FormData(); + form.append('file_hash', fileHash); + form.append('chunk_index', chunkIndex); + form.append('filename', file.name); + form.append('file_size', file.size); + form.append('chunk_size', CHUNK_SIZE); + form.append('chunk_hash', chunkHash); + form.append('chunk', chunk); + + const response = await fetch('/upload/chunk', { + method: 'POST', + body: form + }); + + const result = await response.json(); + + if (result.all_chunk_uploaded) { + console.log('上传完成,文件已合并:', result.file_path); + return true; // 上传完成 + } + + return false; // 继续上传 +} + +// 分片上传主函数 +async function uploadFile(file) { + const fileHash = await calculateFileHash(file); + const chunkSize = 2 * 1024 * 1024; // 2MB + const totalChunks = Math.ceil(file.size / chunkSize); + + for (let i = 0; i < totalChunks; i++) { + const chunk = file.slice(i * chunkSize, (i + 1) * chunkSize); + const chunkHash = await calculateFileHash(chunk); + + const completed = await uploadChunk(file, chunk, { + fileHash, + chunkIndex: i, + chunkHash + }); + + if (completed) { + break; // 上传完成 + } + } +} +``` + +#### 2. cURL 示例 + +```bash +# 上传分片 +curl -X POST http://localhost:8080/upload/chunk \ + -F "file_hash=abc123" \ + -F "chunk_index=0" \ + -F "filename=large_file.zip" \ + -F "file_size=10485760" \ + -F "chunk_size=2097152" \ + -F "chunk_hash=def456" \ + -F "chunk=@chunk_0.part" + +# 检查分片是否存在 +curl "http://localhost:8080/upload/chunk_exists?file_hash=abc123&chunk_index=0&chunk_hash=def456&file_size=10485760&trunk_size=2097152" +``` + +### 核心算法 + +#### 1. 分片合并判断 + +系统使用以下逻辑判断是否所有分片都已上传: + +```v +// 计算理论上需要的分片数量 +expected_chunks := (expected_file_size + u64(chunk_size) - 1) / u64(chunk_size) + +// 只有当分片数量达到预期且总大小 >= 文件大小时,才认为上传完成 +if chunk_count >= int(expected_chunks) && total_chunk_size >= expected_file_size { + all_chunk_uploaded = true +} +``` + +#### 2. 文件去重机制 + +- 基于文件哈希进行去重 +- 相同哈希的文件只存储一份 +- 支持同一文件的不同文件名 + +#### 3. 秒传检测 + +- 前端计算文件哈希 +- 后端检查文件是否已存在 +- 如果存在则直接返回文件信息,无需上传 + +### 性能优化 + +#### 1. 内存管理 + +- 分片文件存储在磁盘,不占用大量内存 +- 上传状态使用内存缓存,提高查询速度 +- 支持配置清理策略,自动清理临时文件 + +#### 2. 并发处理 + +- 支持多用户同时上传 +- 分片级别的并发控制 +- 文件级别的锁机制 + +#### 3. 错误处理 + +- 网络异常自动重试 +- 分片损坏自动重新上传 +- 完整的错误日志记录 + +### 监控和日志 + +#### 1. 调试日志 + +系统提供详细的调试日志,包括: +- 分片上传状态 +- 文件合并过程 +- 错误信息追踪 + +#### 2. 性能监控 + +- 上传速度统计 +- 分片成功率 +- 系统资源使用情况 + +### 故障排除 + +#### 1. 常见问题 + +**Q: 上传大文件时出现内存不足** +A: 检查分片大小配置,建议设置为 1-5MB + +**Q: 分片上传后文件合并失败** +A: 检查磁盘空间和文件权限 + +**Q: 秒传功能不工作** +A: 确认前端哈希算法与后端一致 + +#### 2. 日志分析 + +查看服务器日志获取详细错误信息: +```bash +# 启动时查看详细日志 +v run chunk_upload_example.v +``` + +### 扩展功能 + +#### 1. 云存储集成 + +可以扩展支持: +- AWS S3 +- 阿里云 OSS +- 腾讯云 COS + +#### 2. 文件处理 + +可以添加: +- 图片压缩 +- 视频转码 +- 文档预览 + +#### 3. 权限控制 + +可以集成: +- 用户认证 +- 文件权限 +- 访问控制 + ## 性能特性 - **混合路由算法**:静态路由 O(1) 查找,动态路由正则匹配