跳到主要内容

文件存储

VEF 内置了存储抽象、可配置 provider、内置资源、代理中间件,以及用于 CRUD 流程的自动文件提升能力。

支持的 Provider

storage 模块当前支持:

Provider 值含义
memory内存存储
filesystem本地文件系统存储
minioMinIO 对象存储

如果没有显式配置 provider,模块默认使用 memory

storage.Service 接口

应用代码应依赖 storage.Service,而不是某个具体 provider 实现。

完整公开接口包括:

方法作用
PutObject(ctx, opts)上传单个对象
GetObject(ctx, opts)读取单个对象
DeleteObject(ctx, opts)删除单个对象
DeleteObjects(ctx, opts)批量删除
ListObjects(ctx, opts)列出对象
GetPresignedURL(ctx, opts)生成临时预签名 URL
CopyObject(ctx, opts)复制对象
MoveObject(ctx, opts)移动对象
StatObject(ctx, opts)检查对象元数据
PromoteObject(ctx, tempKey)temp/ 对象提升为永久对象

Option 类型

storage 包为每个操作都暴露了对应 option struct:

Option 类型对应方法
storage.PutObjectOptionsPutObject
storage.GetObjectOptionsGetObject
storage.DeleteObjectOptionsDeleteObject
storage.DeleteObjectsOptionsDeleteObjects
storage.ListObjectsOptionsListObjects
storage.PresignedURLOptionsGetPresignedURL
storage.CopyObjectOptionsCopyObject
storage.MoveObjectOptionsMoveObject
storage.StatObjectOptionsStatObject

内置资源:sys/storage

storage 模块还会注册一个内置 RPC 资源:

资源
sys/storage

当前 action:

Action作用
upload上传单个文件
get_presigned_url生成预签名 URL
delete_temp只允许删除临时对象
stat查询对象元数据
list列出对象

精确 action 契约见 内置资源

临时上传模型

内置上传路径会把对象 key 生成为带 temp/ 前缀的日期分区路径。

相关公共常量:

常量含义
storage.TempPrefix临时上传的 temp/ 前缀
storage.MetadataKeyOriginalFilename保存原始文件名的 metadata key

文件提升(Promoter)

公开的提升抽象是:

类型作用
storage.Promoter[T]在 CRUD 生命周期中自动提升和清理文件引用

Promoter 的场景矩阵:

调用模式含义
newModel != nil && oldModel == nilcreate 场景
newModel != nil && oldModel != nilupdate 场景
newModel == nil && oldModel != nildelete 场景

支持的 metadata 字段类型:

Meta 类型含义
uploaded_file直接文件字段
richtext含资源引用的 HTML 富文本
markdown含资源引用的 Markdown 文本

存储事件

storage 模块会发布这些文件生命周期事件:

事件类型含义
vef.storage.file.promoted文件从临时存储提升到永久存储
vef.storage.file.deleted文件被删除

存储代理中间件

storage 模块还会挂一个 app 级下载代理路由:

路由
/storage/files/<key>

关键行为:

  • 这个路由是普通 app middleware,不是 RPC action
  • 它不会自动继承 API Bearer 认证
  • 它会对 key 做 URL 解码、读取对象、设置 Content-Type、写缓存头,并以流方式输出文件

最小 Service 示例

package avatars

import (
"context"
"strings"

"github.com/coldsmirk/vef-framework-go/storage"
)

func SaveAvatar(ctx context.Context, svc storage.Service) error {
_, err := svc.PutObject(ctx, storage.PutObjectOptions{
Key: "avatars/user-1001.txt",
Reader: strings.NewReader("demo"),
Size: int64(len("demo")),
ContentType: "text/plain",
})

return err
}

实践建议

  • 应用代码里依赖 storage.Service,不要直接依赖 provider 实现
  • temp/ 上传当作中间态,而不是永久对象位置
  • /storage/files/<key> 这条路由应与 RPC 资源分开单独说明
  • 如果模型里有文件引用,优先用 storage.NewPromoter(...) 接入,而不是自己手写清理逻辑

下一步

继续阅读 自定义处理器,如果你要把 storage.Service 和业务化上传流程结合起来,就会接到那里。