# 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) ### 构建运行 ```bash 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: ```bash 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 `(RS256)。 - 服务从 JWT 的 `sub` claim 取用户 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) |