From 369655a9ea65de0d101bb8fce38c94b4cc095067 Mon Sep 17 00:00:00 2001 From: meiseayoung Date: Mon, 29 Dec 2025 18:06:05 +0800 Subject: [PATCH] doc: rewrite README.MD content --- README.md | 1101 ++++++++++++++--------------------------------------- 1 file changed, 278 insertions(+), 823 deletions(-) diff --git a/README.md b/README.md index 5b3b544..43dbf26 100644 --- a/README.md +++ b/README.md @@ -1,47 +1,46 @@ # V-Hono -一个用 V 语言编写的轻量级 Web 框架,灵感来自 Hono.js。 +一个用 V 语言编写的高性能 Web 框架,灵感来自 [Hono.js](https://hono.dev/)。 ## 特性 -- 🚀 高性能路由系统(混合路由算法) +- 🚀 高性能路由系统(FastRouter + LRU 缓存 + 预编译正则) - 🎯 类似 Hono.js 的 Context API - 🔧 洋葱模型中间件支持 - 📦 内置 LRU 缓存系统 -- 🛠️ 支持动态路由参数(`:param`) -- ⚡ 支持通配符路由(`*` 和 `**`) +- 🛠️ 动态路由参数(`:param`)和通配符路由(`*`、`**`) - 🔄 支持所有 HTTP 方法(GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS) -- 🏗️ 构造函数风格的 API 设计 -- 📁 静态文件服务中间件(类似 Hono.js 的 serveStatic) +- 🏗️ 路由分组与子应用中间件继承 +- 📁 静态文件服务中间件 - 🛡️ 内置安全防护(路径遍历攻击防护、点文件访问控制) - 💾 智能缓存支持(ETag、Last-Modified、Cache-Control) -- 🎯 自动 Content-Type 检测 -- 📤 **大文件分片上传**:支持大文件分片上传、断点续传、秒传、自动合并 -- 🔄 **断点续传**:支持上传中断后继续上传 -- ⚡ **秒传功能**:基于文件哈希的秒传检测 -- 🗂️ **文件去重**:基于文件哈希的去重机制 -- 💾 **数据库存储**:使用 SQLite 存储文件元数据 -- 🚀 **双请求池系统**:活跃请求池 + 待请求池,支持并发控制和智能调度 -- ⚡ **并发优化**:可配置最大并发数,自动管理请求队列 -- 📊 **实时状态监控**:实时显示活跃请求数和等待请求数 -- ⚡ **性能优化**:分片大小记录文件,避免遍历文件计算总大小 +- 📤 大文件分片上传(断点续传、秒传、自动合并) +- 🔐 用户认证系统(JWT、角色权限、菜单管理) +- 📊 配置管理系统 +- 🗄️ SQLite 数据库集成 + +## 性能测试 + +基于 100,000 次迭代的路由匹配性能测试: + +| 框架 | 平均延迟 | 吞吐量 | +|------|---------|--------| +| v-hono | 5,621 ns/op | **177,904 ops/sec** | +| 简单路由器 | 9,265 ns/op | 107,933 ops/sec | + +v-hono 在动态路由场景下优势明显,得益于 LRU 缓存和预编译正则表达式。 ## 安装 ```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 @@ -53,14 +52,10 @@ fn main() { return c.html('

Hello V-Hono!

') }) - app.get('/api/users/:id', fn (mut c hono.Context) http.Response { + // 动态路由 + app.get('/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}"}') + return c.json('{"id": "${user_id}"}') }) // 中间件 @@ -73,124 +68,76 @@ fn main() { } ``` -## 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 // 响应体 +// 创建子应用 +mut api := hono.Hono.new() + +// 子应用中间件(只对 /api/* 生效) +api.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response { + c.headers['X-API-Version'] = '1.0' + return next(mut c) +}) + +api.get('/users', fn (mut c hono.Context) http.Response { + return c.json('{"users": []}') +}) + +// 挂载到主应用 +app.route('/api', mut api) +``` + +## all() 方法 + +一次性为所有 HTTP 方法注册处理器: + +```v +app.all('/echo', fn (mut c hono.Context) http.Response { + return c.json('{"method": "${c.req.method}"}') +}) +``` + +## 自定义错误处理 + +```v +// 自定义 404 处理器 +app.not_found(fn (mut c hono.Context) http.Response { + c.status(404) + return c.json('{"error": "Not Found", "path": "${c.path}"}') +}) + +// 自定义错误处理器 +app.on_error(fn (error_msg string, status_code int, mut c hono.Context) http.Response { + c.status(status_code) + return c.json('{"error": "${error_msg}"}') +}) +``` + +## 静态文件服务 + +```v +// 默认配置(./public 目录) +app.use(hono.serve_static_default()) + +// 指定路径前缀和根目录 +app.use(hono.serve_static_path('/assets', './static')) + +// 自定义配置 +options := hono.StaticOptions{ + root: './public' + path: '/static' + index: 'index.html' + etag: true + last_modified: true + max_age: 3600 } +app.use(hono.serve_static(options)) ``` -### 构造函数 - -所有对象都使用构造函数风格创建: - -```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 响应 @@ -202,723 +149,231 @@ return c.text('Hello World') // HTML 响应 return c.html('

Hello

') +// 文件响应 +return c.file('path/to/file.pdf') + // 设置状态码 -c.status(404) -return c.json('{"error": "Not Found"}') +c.status(201) +return c.json('{"created": true}') + +// 错误响应 +return c.not_found('Resource not found') +return c.unauthorized('Token required') +return c.forbidden('Access denied') ``` -### 中间件 -支持洋葱模型中间件: +## 大文件分片上传 + +支持大文件分片上传、断点续传、秒传功能: ```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) +mut upload_manager := hono.new_chunk_upload_manager(hono.ChunkUploadConfig{ + chunk_size: 1024 * 1024 // 1MB 分片 + max_file_size: 1024 * 1024 * 1024 // 最大 1GB + temp_dir: './uploads/chunks' + upload_dir: './uploads/files' }) -// 认证中间件 -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.post('/upload/chunk', fn [mut upload_manager] (mut c hono.Context) http.Response { + return upload_manager.handle_chunk_upload(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 +// 上传状态查询 +app.get('/upload/status', fn [mut upload_manager] (mut c hono.Context) http.Response { + return upload_manager.get_upload_status(mut c) }) ``` -### 静态文件服务 - -提供类似 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 框架构建的高性能文件上传解决方案。支持大文件分片上传、断点续传、秒传、自动合并等功能。 - -**相关文档:** -- [分片上传系统详解](README_CHUNK_UPLOAD.md) - 完整的分片上传功能文档 -- [双请求池系统详解](README_REQUEST_POOL.md) - 请求池系统的设计和实现 - -### 主要特性 - -- ✅ **分片上传**:支持大文件分片上传,默认分片大小 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 -// 请求池管理类 -class RequestPool { - constructor(maxConcurrent = 6) { - this.maxConcurrent = maxConcurrent; - this.activePool = new Map(); // 活跃请求池 {requestId: Promise} - this.pendingPool = []; // 待请求池 [{requestId, task, resolve, reject}] - this.requestIdCounter = 0; - } +// 请求池管理(控制并发) +const requestPool = new RequestPool(6); - // 生成请求ID - generateRequestId() { - return ++this.requestIdCounter; - } - - // 添加请求到池中 - async addRequest(task) { - const requestId = this.generateRequestId(); - - return new Promise((resolve, reject) => { - const request = { - requestId, - task, - resolve, - reject - }; - - // 如果活跃池未满,直接执行 - if (this.activePool.size < this.maxConcurrent) { - this.executeRequest(request); - } else { - // 否则加入待请求池 - this.pendingPool.push(request); - console.log(`请求 ${requestId} 加入待请求池,当前待请求数: ${this.pendingPool.length}`); - } - }); - } - - // 执行请求 - async executeRequest(request) { - const { requestId, task, resolve, reject } = request; - - console.log(`开始执行请求 ${requestId},当前活跃请求数: ${this.activePool.size + 1}`); - - // 将请求添加到活跃池 - const promise = task() - .then(result => { - console.log(`请求 ${requestId} 执行成功`); - resolve(result); - return result; - }) - .catch(error => { - console.log(`请求 ${requestId} 执行失败:`, error); - reject(error); - throw error; - }) - .finally(() => { - // 请求完成后从活跃池移除 - this.activePool.delete(requestId); - console.log(`请求 ${requestId} 完成,从活跃池移除,当前活跃请求数: ${this.activePool.size}`); - - // 检查待请求池,如果有待请求则执行 - this.processPendingRequests(); - }); - - this.activePool.set(requestId, promise); - } - - // 处理待请求池中的请求 - processPendingRequests() { - while (this.pendingPool.length > 0 && this.activePool.size < this.maxConcurrent) { - const request = this.pendingPool.shift(); - this.executeRequest(request); - console.log(`从待请求池取出请求 ${request.requestId} 执行,剩余待请求数: ${this.pendingPool.length}`); - } - } - - // 获取池状态 - getStatus() { - return { - activeCount: this.activePool.size, - pendingCount: this.pendingPool.length, - maxConcurrent: this.maxConcurrent - }; - } - - // 清空所有池 - clear() { - this.activePool.clear(); - this.pendingPool = []; - console.log('请求池已清空'); - } -} - -// 计算文件哈希 -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 uploadFileWithPool(file) { - const fileHash = await calculateFileHash(file); - const chunkSize = 2 * 1024 * 1024; // 2MB - const totalChunks = Math.ceil(file.size / chunkSize); - - // 创建请求池实例 - const requestPool = new RequestPool(6); - - // 创建所有分片的上传任务并添加到请求池 - const uploadPromises = []; - for (let i = 0; i < totalChunks; i++) { - const chunkIndex = i; - const promise = requestPool.addRequest(async () => { - const chunk = file.slice(chunkIndex * chunkSize, (chunkIndex + 1) * chunkSize); - const chunkHash = await calculateFileHash(chunk); - - return await uploadChunk(file, chunk, { - fileHash, - chunkIndex, - chunkHash - }); - }); - - uploadPromises.push(promise); - } - - // 等待所有上传任务完成 - const results = await Promise.all(uploadPromises); - - // 清空请求池 - requestPool.clear(); - - return results; + const response = await fetch('/upload/chunk', { method: 'POST', body: form }); + return response.json(); } ``` -#### 2. cURL 示例 +详细文档:[README_CHUNK_UPLOAD.md](README_CHUNK_UPLOAD.md) + +## 用户认证系统 + +内置完整的用户认证和权限管理: + +```v +// 初始化认证管理器 +mut db := hono.new_database_manager('./auth.db') +mut auth := hono.new_auth_manager(db) +auth.init_tables()! + +// 创建用户 +user := auth.create_user('admin', 'admin@example.com', 'password', .admin)! + +// 用户登录 +session := auth.login('admin', 'password')! + +// 验证令牌 +user := auth.verify_token(session.token)! + +// 权限检查 +if auth.check_permission(user, 'write') { + // 允许操作 +} +``` + +### 用户角色 + +- `admin` - 管理员(所有权限) +- `manager` - 管理者(read、write、manage) +- `user` - 普通用户(read、write) +- `guest` - 访客(read) + +详细文档:[README_AUTH_SYSTEM.md](README_AUTH_SYSTEM.md) + +## 配置管理 + +```v +// 加载配置 +config := hono.load_config('config.json')! + +// 获取配置值 +db_path := config.get_string('database.path') +port := config.get_int('server.port') +debug := config.get_bool('debug') +``` + +## 项目结构 + +``` +v-hono/ +├── hono/ # 核心模块 +│ ├── app.v # 主应用 +│ ├── fast_router.v # 快速路由器 +│ ├── cache.v # LRU 缓存 +│ ├── auth.v # 认证系统 +│ ├── database.v # 数据库管理 +│ ├── upload.v # 分片上传 +│ ├── static.v # 静态文件服务 +│ ├── response.v # 响应方法 +│ ├── error_handler.v # 错误处理 +│ ├── security.v # 安全模块 +│ └── ... +├── public/ # 静态文件目录 +├── uploads/ # 上传文件目录 +├── example.v # 完整示例 +├── route_grouping_example_v2.v # 路由分组示例 +└── benchmark_vweb_vs_hono.v # 性能测试 +``` + +## API 参考 + +### Context + +```v +pub struct Context { +pub: + req http.Request // 原始请求 + params map[string]string // 路径参数 + query map[string]string // 查询参数 + url string // 请求 URL + path string // 请求路径 +pub mut: + status_code int = 200 // 响应状态码 + headers map[string]string // 响应头 + body string // 请求体 +} +``` + +### 路由方法 + +```v +app.get(path, handler) // GET 请求 +app.post(path, handler) // POST 请求 +app.put(path, handler) // PUT 请求 +app.delete(path, handler) // DELETE 请求 +app.patch(path, handler) // PATCH 请求 +app.head(path, handler) // HEAD 请求 +app.options(path, handler) // OPTIONS 请求 +app.all(path, handler) // 所有方法 +``` + +### 动态路由 + +```v +// 单参数 +app.get('/users/:id', handler) + +// 多参数 +app.get('/users/:id/posts/:post_id', handler) + +// 通配符 +app.get('/files/*path', handler) +``` + +### 中间件 + +```v +// 全局中间件 +app.use(middleware) + +// 子应用中间件(只对特定前缀生效) +mut api := hono.Hono.new() +api.use(api_middleware) +app.route('/api', mut api) +``` + +## 运行示例 ```bash -# 上传分片 +# 完整示例 +v run example.v + +# 路由分组示例 +v run route_grouping_example_v2.v + +# 性能测试 +v run benchmark_vweb_vs_hono.v +``` + +## 测试命令 + +```bash +# 基本测试 +curl http://localhost:8080/ +curl http://localhost:8080/api/health +curl http://localhost:8080/users/123 + +# POST 请求 +curl -X POST http://localhost:8080/api/users -d '{"name":"John"}' + +# 静态文件 +curl http://localhost:8080/test.txt + +# 分片上传 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" + -F "chunk=@file.part" ``` -### 核心算法 +## 相关文档 -#### 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. 双请求池系统 - -**架构设计:** -- **活跃请求池**:存储正在执行的请求,使用 Map 结构 `{requestId: Promise}` -- **待请求池**:存储等待执行的请求,使用数组结构 `[{requestId, task, resolve, reject}]` -- **智能调度**:当活跃池未满时直接执行,满时加入待请求池 -- **自动管理**:请求完成后自动从活跃池移除,并处理待请求池中的请求 - -**核心特性:** -- **可配置并发数**:默认最大并发数为 6,可根据服务器性能调整 -- **请求ID管理**:每个请求都有唯一ID,便于追踪和调试 -- **状态实时更新**:实时显示活跃请求数和等待请求数 -- **自动清理**:上传完成后自动清空请求池 - -**使用示例:** -```javascript -// 创建请求池实例(最大并发6个) -const requestPool = new RequestPool(6); - -// 添加请求到池中 -const promise = requestPool.addRequest(async () => { - // 上传分片的逻辑 - return await uploadChunk(chunk); -}); - -// 获取池状态 -const status = requestPool.getStatus(); -console.log(`活跃: ${status.activeCount}/${status.maxConcurrent}, 等待: ${status.pendingCount}`); -``` - -#### 3. 并发处理 - -- 支持多用户同时上传 -- 分片级别的并发控制 -- 文件级别的锁机制 -- 基于请求池的智能并发管理 - -#### 4. 错误处理 - -- 网络异常自动重试 -- 分片损坏自动重新上传 -- 完整的错误日志记录 -- 请求池级别的错误隔离 - -### 监控和日志 - -#### 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. 权限控制 - -可以集成: -- 用户认证 -- 文件权限 -- 访问控制 - -#### 4. 双请求池系统 - -详细文档请参考: -- [双请求池系统详解](README_REQUEST_POOL.md) - 完整的系统设计和实现指南 -- [分片上传系统](README_CHUNK_UPLOAD.md) - 包含请求池的分片上传完整方案 - -## 性能特性 - -- **混合路由算法**:静态路由 O(1) 查找,动态路由正则匹配 -- **LRU 缓存**:自动缓存路由匹配结果 -- **Trie 路由树**:支持复杂路径匹配 -- **内存优化**:使用指针和堆分配优化性能 -- **分片上传优化**:分片大小记录文件,避免遍历文件计算总大小(O(1) vs O(n)) - -## 贡献 - -欢迎提交 Issue 和 Pull Request! +- [分片上传系统](README_CHUNK_UPLOAD.md) +- [请求池系统](README_REQUEST_POOL.md) +- [认证系统](README_AUTH_SYSTEM.md) +- [数据库模块](README_DATABASE.md) +- [性能测试](README_BENCHMARK.md) ## 许可证 MIT License -