From e5c8bad8710546dc030eed0bfa2d297990dc0833 Mon Sep 17 00:00:00 2001
From: yangshangbo <42314632+yangshangbo@users.noreply.github.com>
Date: Fri, 11 Jul 2025 10:16:05 +0800
Subject: [PATCH] fix: change README.MD to README.md
---
README.md | 768 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 768 insertions(+)
create mode 100644 README.md
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d400b45
--- /dev/null
+++ b/README.md
@@ -0,0 +1,768 @@
+# V-Hono
+
+一个用 V 语言编写的轻量级 Web 框架,灵感来自 Hono.js。
+
+## 特性
+
+- 🚀 高性能路由系统(混合路由算法)
+- 🎯 类似 Hono.js 的 Context API
+- 🔧 洋葱模型中间件支持
+- 📦 内置 LRU 缓存系统
+- 🛠️ 支持动态路由参数(`:param`)
+- ⚡ 支持通配符路由(`*` 和 `**`)
+- 🔄 支持所有 HTTP 方法(GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS)
+- 🏗️ 构造函数风格的 API 设计
+- 📁 静态文件服务中间件(类似 Hono.js 的 serveStatic)
+- 🛡️ 内置安全防护(路径遍历攻击防护、点文件访问控制)
+- 💾 智能缓存支持(ETag、Last-Modified、Cache-Control)
+- 🎯 自动 Content-Type 检测
+- 📤 **大文件分片上传**:支持大文件分片上传、断点续传、秒传、自动合并
+- 🔄 **断点续传**:支持上传中断后继续上传
+- ⚡ **秒传功能**:基于文件哈希的秒传检测
+- 🗂️ **文件去重**:基于文件哈希的去重机制
+- 💾 **数据库存储**:使用 SQLite 存储文件元数据
+
+## 安装
+
+```bash
+# 克隆仓库
+git clone https://github.com/meiseayoung/v-hono.git
+cd v-hono
+
+# 运行示例
+v run example.v
+```
+
+## 快速开始
+
+```v
+module main
+
+import hono
+import net.http
+
+fn main() {
+ mut app := hono.Hono.new()
+
+ // 基本路由
+ app.get('/', fn (mut c hono.Context) http.Response {
+ return c.html('
Hello V-Hono!
')
+ })
+
+ app.get('/api/users/:id', fn (mut c hono.Context) http.Response {
+ user_id := c.params['id']
+ return c.json('{"id": "${user_id}", "name": "John Doe"}')
+ })
+
+ app.post('/api/users', fn (mut c hono.Context) http.Response {
+ c.status(201)
+ return c.json('{"message": "User created", "data": "${c.body}"}')
+ })
+
+ // 中间件
+ app.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response {
+ println('[LOG] ${c.path}')
+ return next(mut c)
+ })
+
+ app.listen(':3000')
+}
+```
+
+## API 参考
+
+### Context
+
+Context 是请求的核心对象,包含所有请求和响应信息:
+
+```v
+pub struct Context {
+pub:
+ req http.Request // 原始 HTTP 请求
+ params map[string]string // 路径参数
+ query map[string]string // 查询参数
+ url string // 请求 URL
+pub mut:
+ status_code int = 200 // 响应状态码
+ headers map[string]string // 响应头
+ body string // 响应体
+}
+```
+
+### 构造函数
+
+所有对象都使用构造函数风格创建:
+
+```v
+// 创建 Context
+ctx := hono.Context.new(req, params, query, body)
+
+// 创建路由器
+router := hono.ContextHybridRouter.new()
+trie_router := hono.ContextTrieRouter.new()
+
+// 创建缓存
+cache := hono.ContextLRUCache.new(1000)
+```
+
+### 路由方法
+
+支持所有 HTTP 方法:
+
+```v
+// GET 请求
+app.get('/path', fn (mut c hono.Context) http.Response {
+ return c.json('{"message": "GET response"}')
+})
+
+// POST 请求
+app.post('/path', fn (mut c hono.Context) http.Response {
+ return c.json('{"message": "POST response"}')
+})
+
+// PUT 请求
+app.put('/path', fn (mut c hono.Context) http.Response {
+ return c.json('{"message": "PUT response"}')
+})
+
+// DELETE 请求
+app.delete('/path', fn (mut c hono.Context) http.Response {
+ c.status(204)
+ return c.text('')
+})
+
+// PATCH 请求
+app.patch('/path', fn (mut c hono.Context) http.Response {
+ return c.json('{"message": "PATCH response"}')
+})
+
+// HEAD 请求
+app.head('/path', fn (mut c hono.Context) http.Response {
+ return c.text('')
+})
+
+// OPTIONS 请求
+app.options('/path', fn (mut c hono.Context) http.Response {
+ return c.text('')
+})
+```
+
+### 动态路由
+
+支持路径参数和通配符:
+
+```v
+// 单参数
+app.get('/users/:id', fn (mut c hono.Context) http.Response {
+ user_id := c.params['id']
+ return c.json('{"id": "${user_id}"}')
+})
+
+// 多参数
+app.get('/posts/:id/comments/:comment_id', fn (mut c hono.Context) http.Response {
+ post_id := c.params['id']
+ comment_id := c.params['comment_id']
+ return c.json('{"post_id": "${post_id}", "comment_id": "${comment_id}"}')
+})
+
+// 嵌套参数
+app.get('/api/users/:user_id/posts/:post_id', fn (mut c hono.Context) http.Response {
+ user_id := c.params['user_id']
+ post_id := c.params['post_id']
+ return c.json('{"user_id": "${user_id}", "post_id": "${post_id}"}')
+})
+
+// 通配符
+app.get('/files/*', fn (mut c hono.Context) http.Response {
+ file_path := c.params['*']
+ return c.text('File: ${file_path}')
+})
+
+// 多级通配符
+app.get('/api/**/search', fn (mut c hono.Context) http.Response {
+ return c.json('{"search": "wildcard"}')
+})
+```
+
+### 响应方法
+
+所有响应方法直接返回 `http.Response`:
+
+```v
+// JSON 响应
+return c.json('{"message": "Hello"}')
+
+// 文本响应
+return c.text('Hello World')
+
+// HTML 响应
+return c.html('Hello
')
+
+// 设置状态码
+c.status(404)
+return c.json('{"error": "Not Found"}')
+```
+
+### 中间件
+
+支持洋葱模型中间件:
+
+```v
+// 日志中间件
+app.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response {
+ println('[${time.now()}] ${c.req.method} ${c.path}')
+ return next(mut c)
+})
+
+// 认证中间件
+app.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response {
+ token := c.query['token']
+ if token == '' {
+ c.status(401)
+ return c.json('{"error": "Unauthorized"}')
+ }
+ return next(mut c)
+})
+
+// 性能监控中间件
+app.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response {
+ start := time.now()
+ response := next(mut c)
+ duration := time.now() - start
+ println('请求处理时间: ${duration.milliseconds()}ms')
+ return response
+})
+```
+
+### 静态文件服务
+
+提供类似 Hono.js 的 `serveStatic` 中间件:
+
+```v
+// 使用默认配置(./public 目录)
+app.use(hono.serve_static_default())
+
+// 指定根目录
+app.use(hono.serve_static_root('./static'))
+
+// 指定路径前缀和根目录
+app.use(hono.serve_static_path('/assets', './static'))
+
+// 使用自定义配置
+options := hono.StaticOptions{
+ root: './public'
+ path: '/static'
+ index: 'index.html'
+ dotfiles: false
+ etag: true
+ last_modified: true
+ max_age: 3600 // 1小时缓存
+ headers: {
+ 'X-Custom-Header': 'value'
+ }
+}
+app.use(hono.serve_static(options))
+```
+
+**特性:**
+- 🛡️ 路径遍历攻击防护
+- 📁 自动索引文件支持(index.html)
+- 🔒 点文件访问控制
+- 🏷️ ETag 和 Last-Modified 支持
+- 💾 可配置缓存策略
+- 📋 自定义响应头
+- 🎯 智能 Content-Type 检测
+
+### 查询参数和请求体
+
+```v
+// 获取查询参数
+app.get('/search', fn (mut c hono.Context) http.Response {
+ query := c.query['q']
+ page := c.query['page']
+ return c.json('{"query": "${query}", "page": "${page}"}')
+})
+
+// 获取请求体
+app.post('/api/data', fn (mut c hono.Context) http.Response {
+ println('请求体: ${c.body}')
+ return c.json('{"received": "${c.body}"}')
+})
+```
+
+### 路由统计和缓存
+
+```v
+// 获取路由统计信息
+static_count, dynamic_count, cache_size, cache_capacity := app.get_router_stats()
+println('静态路由: ${static_count}, 动态路由: ${dynamic_count}')
+println('缓存: ${cache_size}/${cache_capacity}')
+
+// 清理缓存
+app.clear_cache()
+```
+
+## 示例
+
+### 基础示例
+
+运行完整示例:
+
+```bash
+v run example.v
+```
+
+示例包含:
+- 静态路由
+- 动态路由(单参数、多参数、嵌套参数)
+- 所有 HTTP 方法
+- 中间件功能
+- 参数提取和查询参数处理
+
+### 静态文件服务示例
+
+运行静态文件服务示例:
+
+```bash
+v run static_example.v
+```
+
+这个示例演示了:
+- 多个静态文件目录配置
+- 自定义缓存策略
+- 安全特性演示
+- 完整的HTML界面
+
+**重要提示**: 静态文件路径映射
+- `./public` 目录 → 访问路径 `/`
+- `./static` 目录 → 访问路径 `/assets`
+- `./uploads` 目录 → 访问路径 `/files`
+
+例如:
+- `./public/test.txt` → `http://localhost:8080/test.txt`
+- `./static/style.css` → `http://localhost:8080/assets/style.css`
+- `./uploads/sample.json` → `http://localhost:8080/files/sample.json`
+
+### 快速测试
+
+运行简单的静态文件测试:
+
+```bash
+v run test_static.v
+```
+
+测试文件结构:
+```
+v-hono/
+├── public/
+│ ├── index.html # 默认索引文件
+│ └── test.txt # 测试文本文件
+├── static/
+│ ├── style.css # 样式文件
+│ └── app.js # JavaScript文件
+└── uploads/
+ └── 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 https://github.com/meiseayoung/v-hono.git
+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) 查找,动态路由正则匹配
+- **LRU 缓存**:自动缓存路由匹配结果
+- **Trie 路由树**:支持复杂路径匹配
+- **内存优化**:使用指针和堆分配优化性能
+
+## 贡献
+
+欢迎提交 Issue 和 Pull Request!
+
+## 许可证
+
+MIT License
+