middleware
中间件文档
CloudFS 中间件通过 cloudfs.WrapFunc 包装已有的 cloudfs.FS,用于在不修改底层驱动实现的前提下增加路径映射、缓存、限速、压缩、加密等能力。
使用方式
1base, err := local.New(&local.Option{Path: "/data"}) 2if err != nil { 3 panic(err) 4} 5 6fs, err := middleware.NewFS( 7 base, 8 middleware.PrefixFS("/team-a"), 9 middleware.CacheFS(&middleware.CacheOption{ExpireTime: 60}), 10)
middleware.NewFS 实际上等价于 cloudfs.New。
中间件列表
CacheFS
用于缓存目录列表,减少重复 List() 调用。
1fs, err := middleware.NewFS( 2 base, 3 middleware.CacheFS(&middleware.CacheOption{ExpireTime: 60}), 4)
参数:
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
expire_time |
time.Duration |
60 |
缓存有效时长,代码中按“秒”使用 |
行为说明:
List()会按路径缓存目录结果Stat()会优先从父目录缓存中查找目标项Create()、Rename()、Move()、Copy()、MakeDir()、Remove()会主动失效相关目录缓存- 当
ExpireTime <= 0时,内部会回退到60
注意事项:
- 当前缓存只缓存目录项,不缓存文件内容
ExpireTime字段类型是time.Duration,但实现里按秒处理,因此传入60表示 60 秒,传入time.Minute会得到更大的数值
RateLimitFS
用于限制调用频率,适合 API 限流较严格的远程驱动。
1fs, err := middleware.NewFS( 2 base, 3 middleware.RateLimitFS(&middleware.RateLimitOption{ 4 Wait: true, 5 Burst: 10, 6 Limit: 1, 7 }), 8)
参数:
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
wait |
bool |
false |
触发限流时是否等待令牌 |
burst |
int |
100 |
桶容量 |
limit |
time.Duration |
1 |
单个令牌的时间间隔,代码中按秒处理 |
行为说明:
- 会拦截
List、Copy、Move、Rename、Remove、MakeDir、Stat、Open、Create - 当
Wait=false且无可用令牌时,返回错误访问频率限制 - 当
Wait=true时,会阻塞直到拿到令牌或context.Context取消
注意事项:
- 默认值相当于“每秒约 1 次补充,桶容量 100”
Limit不是 QPS,而是令牌补充间隔
CompressFS
对写入内容进行 gzip 压缩,对读取内容进行 gzip 解压。
1fs, err := middleware.NewFS( 2 base, 3 middleware.CompressFS(&middleware.CompressOption{ 4 Level: gzip.BestCompression, 5 }), 6)
参数:
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
level |
int |
gzip.BestCompression |
gzip 压缩级别 |
行为说明:
Create()返回的 writer 会把写入内容压缩后再交给底层驱动Open()返回的 reader 会自动解压底层内容
注意事项:
- 底层存储的是压缩后的二进制内容,不会自动追加
.gz后缀 - 只适合由同一层中间件读写的场景
EncryptFS
对文件内容以及路径名做透明加密/解密。
1fs, err := middleware.NewFS( 2 base, 3 middleware.EncryptFS(&middleware.EncryptOption{ 4 Password: "secret", 5 PasswordSalt: "secret-salt", 6 Mode: "CFB", 7 DirName: true, 8 FileName: true, 9 Version: "v2", 10 }), 11)
参数:
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
mode |
string |
CFB |
加密模式,可选 CTR、OFB、其他值回退 CFB |
dir_name |
bool |
false |
是否加密目录名 |
file_name |
bool |
false |
是否加密文件名 |
suffix |
string |
"" |
不加密文件名时,为底层文件追加后缀 |
password |
string |
无 | 必填,加密密码 |
password_salt |
string |
与 password 相同 |
生成 IV 使用 |
version |
string |
v1 兼容模式 |
v2 使用新版 IV 生成方式 |
行为说明:
- 文件内容通过流式 AES 加密/解密
- 可分别控制目录名和文件名是否加密
suffix常用于“不加密文件名但隐藏真实扩展名”的场景Stat()因为无法预先判断路径是文件还是目录,必要时会尝试两次
注意事项:
password为空时会直接返回错误Version不是校验字段,只有v2会启用新版 IV,其余值都走兼容逻辑- 修改
Password、PasswordSalt、Mode、Version后,旧数据可能无法再正常读取
HookFS
提供完全自定义的路径映射和文件信息映射能力,适合高级场景。
1fs, err := middleware.NewFS( 2 base, 3 middleware.HookFS(&middleware.HookOption{ 4 PathFn: func(path string) string { 5 return "/tenant-a" + path 6 }, 7 FileFn: func(file cloudfs.FileInfo) (cloudfs.FileInfo, bool) { 8 return file, true 9 }, 10 }), 11)
参数:
| 字段 | 类型 | 说明 |
|---|---|---|
PathFn |
func(string) string |
将外部路径转换为底层路径 |
FileFn |
func(cloudfs.FileInfo) (cloudfs.FileInfo, bool) |
转换文件信息,bool=false 时会过滤掉当前文件 |
行为说明:
PathFn会应用于List、Stat、Open、Create、Copy、Move、Rename、Remove、MakeDirFileFn会应用于List和Stat- 这是代码级扩展点,不适合通过 JSON 配置动态构造
PrefixFS
给底层驱动自动追加固定前缀,是 HookFS 的常见封装。
1fs, err := middleware.NewFS(base, middleware.PrefixFS("/project-a"))
效果:
- 访问
/docs/a.txt时,底层会访问/project-a/docs/a.txt - 对外暴露的路径会自动去掉该前缀
TrimPrefixFS
把外部路径上的指定前缀裁剪后再传给底层驱动。
1fs, err := middleware.NewFS(base, middleware.TrimPrefixFS(base, "/mnt"))
效果:
- 对外访问
/mnt/docs/a.txt - 底层实际访问
/docs/a.txt
注意事项:
- 这个函数签名为
TrimPrefixFS(fs cloudfs.FS, prefix string),但返回值仍然是cloudfs.WrapFunc - 常见用途是兼容某些底层驱动不接受以
/开头的路径,例如 SMB 驱动内部就使用了它
组合建议
推荐组合顺序:
- 路径类中间件:
PrefixFS、TrimPrefixFS、HookFS - 内容类中间件:
EncryptFS、CompressFS - 行为类中间件:
CacheFS、RateLimitFS
原因:
- 先统一路径,再处理内容,最后做缓存和限流,通常更容易理解和排查问题
- 如果同时使用加密和压缩,常见顺序是“先压缩再加密”
已知限制
- 中间件没有统一的动态注册机制,通常需要在代码中显式拼装
HookOption包含函数,不能直接从 JSON 动态反序列化CacheOption和RateLimitOption的time.Duration字段当前实现都按秒使用