- Replace Go (Kratos/Wire/Watermill) implementation with .NET 10 solution - Add FileSystem.slnx, Directory.Build.props and ABP module structure - Keep Docker/Jenkins/docker-compose deployment artifacts - This drops all Go sources (cmd/, internal/, api/proto) in favor of src/
5.8 KiB
5.8 KiB
file-system
基于 .NET 10 + ABP + FastEndpoints + MediatR + EF Core 的文件存储微服务,提供文件上传/下载、分片上传、存储桶管理、文件夹/文件元数据管理与分享等能力。底层使用 S3 兼容存储(RustFS/MinIO)与 PostgreSQL。提供 HTTP + gRPC 双协议。
由原 Go/Kratos 版重写而来,功能 1:1 对齐。
功能特性
- ✅ 文件操作:上传、下载、删除、列表、预签名预览(24h)、文本内容预览(10MB)
- ✅ 分片上传:大文件分片上传(init / part / complete / abort,服务端按 PartNumber 排序)
- ✅ 存储桶管理:创建、列表、删除
- ✅ 文件夹与元数据:树形文件夹、文件元数据、移动、递归 CTE 事务删除 + best-effort S3 清理
- ✅ 分享链接:bcrypt 密码保护、过期时间、下载次数限制
- ✅ JWT 鉴权:OIDC/JWKS,信 SSO 签发的 RS256 JWT
- ✅ 所有权隔离:受保护接口以 JWT
sub为权威所有者,忽略请求体owner_id - ✅ 双协议:HTTP(FastEndpoints)+ gRPC(原生 proto),共享同一套 MediatR CQRS
技术栈
| 类别 | 技术 |
|---|---|
| 框架 | .NET 10、ABP 10.3(模块化)、FastEndpoints 8.1(REPR)、MediatR 14(CQRS) |
| 数据 | EF Core 10 + PostgreSQL(Npgsql) |
| 存储 | AWSSDK.S3(ForcePathStyle,兼容 RustFS/MinIO) |
| 鉴权 | OIDC Authority(JWKS 自动发现) |
| 校验 | FluentValidation 12(管线自动校验) |
| 协议 | HTTP(FastEndpoints)+ gRPC(原生 proto) |
架构分层
FileSystem.Api → FastEndpoints + Middleware + ABP 模块装配 + gRPC 服务
FileSystem.Application → MediatR CQRS handlers + DTOs + Validators + 领域事件
FileSystem.Infrastructure → EF Core + DbContext + Configurations + AuditInterceptor + S3 存储
FileSystem.Domain → Entities + Interfaces + Exceptions(零外部依赖)
依赖:Api → Application → Infrastructure → Domain。ABP 模块 [DependsOn] 链同构 rag-backend。
快速开始
环境要求
- .NET 10 SDK
- PostgreSQL(含库
file_system) - S3 兼容存储(RustFS / MinIO)
- 可访问的 SSO 服务(用于 JWKS)
构建运行
dotnet build
cd src/FileSystem.Infrastructure && dotnet ef migrations add Init --startup-project ../FileSystem.Api
cd src/FileSystem.Api && dotnet run # HTTP :8080, gRPC :9090
或 Docker:
docker compose up -d
服务监听:
- HTTP:
http://localhost:8080 - gRPC:
localhost:9090 - Swagger:
http://localhost:8080/swagger
配置
运行配置在 src/FileSystem.Api/appsettings.json,环境变量用 __ 分隔节覆盖(如 S3__Endpoint → S3:Endpoint)。
| 配置项 | 说明 |
|---|---|
Kestrel:Endpoints:Http/Grpc |
HTTP(8080, Http1)/ gRPC(9090, Http2)监听地址 |
Kestrel:Limits:MaxRequestBodySize |
请求体上限(100 MiB) |
ConnectionStrings:Default |
PostgreSQL 连接串 |
S3:* |
S3 兼容端点与凭证 |
Jwt:Authority |
SSO 地址,框架自动发现 JWKS |
Jwt:RequireHttps |
是否要求 HTTPS(开发期 false) |
鉴权
- 受保护接口要求请求头携带 SSO 下发的
Authorization: Bearer <token>(RS256)。 - 服务从 JWT 的
subclaim 取用户 ID 作为资源所有者;请求体的owner_id/created_by字段仅 proto 兼容保留,不作为鉴权依据。 - 公开接口(无需鉴权):
GetShareInfo、DownloadShare。 - 分享下载在设置了密码时,必须通过
password字段并经 bcrypt 校验。
API 端点(24 个)
| HTTP | 路径 | 鉴权 | 说明 |
|---|---|---|---|
| POST | /api/files/upload |
登录 | 上传文件(multipart,≤100MiB) |
| GET | /api/files/download |
登录 | 下载对象 |
| GET | /api/files/list |
登录 | 分页列对象(默认100/上限1000) |
| GET | /api/files/preview |
登录 | 24h 预签名预览 URL |
| GET | /api/files/content |
登录 | 文本内容(≤10MB) |
| DELETE | /api/files/delete |
登录 | 删除对象 |
| POST | /api/files/multipart/init |
登录 | 开启分片会话 |
| PUT | /api/files/multipart/part |
登录 | 上传单片 |
| POST | /api/files/multipart/complete |
登录 | 合并分片 |
| POST | /api/files/multipart/abort |
登录 | 取消会话 |
| POST | /api/buckets |
登录 | 建桶 |
| GET | /api/buckets |
登录 | 列桶 |
| DELETE | /api/buckets |
登录 | 删桶 |
| POST | /api/folders |
登录+sub | 建文件夹(parent_id 空=根) |
| GET | /api/folders/tree |
登录+sub | 该用户所有文件夹 |
| GET | /api/folders/{id} |
登录+sub | 文件夹+子项 |
| PUT | /api/folders/{id} |
登录+sub | 重命名 |
| DELETE | /api/folders/{id} |
登录+sub | 递归删 DB 子树 + best-effort 删 S3 |
| POST | /api/folders/{folder_id}/files |
登录+sub | UUID key 上传到 files 桶 + 元数据 |
| POST | /api/files/{id}/move |
登录+sub | 移动文件(target 空=根) |
| POST | /api/share |
登录+sub | 建分享(bcrypt 密码/过期/次数) |
| DELETE | /api/share/{id} |
登录+sub | 按 id+owner 删 |
| GET | /api/share/{token} |
公开 | 查分享信息 |
| POST | /api/share/{token}/download |
公开 | 校验密码/过期/次数 → 15min 预签名 URL |
gRPC 方法同上,契约见 src/FileSystem.Api/Protos/file.proto。
错误码
| 错误 | HTTP | 含义 |
|---|---|---|
| Unauthorized | 401 | 缺少/无效 JWT |
| Forbidden | 403 | 无权访问他人资源 |
| InvalidParameter | 400 | 参数校验失败 |
| NotFound | 404 | 资源不存在 |
| AlreadyExists | 409 | 资源已存在 |
| ShareExpired | 410 | 分享已过期 |
| ShareExhausted | 410 | 分享下载次数耗尽 |
| SharePassword | 401 | 分享密码缺失或错误 |
| TooLarge | 413 | 超过单文件大小上限(100MiB) |