769 lines
18 KiB
Markdown
769 lines
18 KiB
Markdown
# 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/your-username/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('<h1>Hello V-Hono!</h1>')
|
||
})
|
||
|
||
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('<h1>Hello</h1>')
|
||
|
||
// 设置状态码
|
||
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 <repository-url>
|
||
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
|
||
|