本地文件存储(Aegis.FileManager.Local)
Local 是 Aegis.FileManager 基础包内置的本地文件系统存储实现,把文件读写落到进程可直写的磁盘目录。纯 System.IO 实现,零网络依赖,不计算 MD5。
StorageKey(文件路径)的拼装规则属于业务领域,详见 文件管理 — 核心概念:业务自拼 Key。本页只讲 Local 后端的接入与配置。
组件概览
| 字段 | 说明 |
|---|---|
| 组件名称 | 本地文件存储 |
| 真实类库 | Aegis.FileManager(基础包内置 Local Provider,无独立 NuGet 包) |
| 最新版本 | 3.1.4-preview4 |
| 组件定位 | 本地文件系统存储实现,轻量默认 Provider |
| 引入方式 | 引用基础包即具备;Component.deps.json 声明 LocalFileManager + 配置 LocalStorage 节启用 |
| 核心能力 | IFileManager 实现、IFileStorage 目录管理、零网络依赖、不算 MD5 |
| 主要配置 | LocalStorage |
| 典型配套 | 文件管理基础层(Aegis.FileManager)、诊断组件(Aegis.Diagnostics) |
什么时候要用它
适合场景:
- 单机部署,文件直接写本地磁盘就够
- 容器部署,写 Pod 本地盘或挂载卷(emptyDir / hostPath / PVC 皆可)
- 开发、测试环境需要一个零依赖的文件存储,不搭 MinIO 也不挂 NAS
- 不需要文件 MD5,
ContentMd5为空可接受
不适合场景:
- 多节点部署需要共享文件(用 S3 对象存储,或各节点挂载同一 NAS 用 NAS 本地存储)
- 需要客户端直连下载 URL(Local 的
GetFileUriAsync返回的是服务器本地路径,浏览器拿不到)
显式 Provider,不是隐式兜底
Local 不会在「什么都没配」时自动生效。启用它必须同时满足两个条件:
Component.deps.json的Load列表声明LocalFileManagerappsettings.json配置LocalStorage节(RootPath必填)
两项缺一,Local 都不注册,应用行为与未引入时完全一致。声明了组件但没配节,启动横幅会直接报 Failure(见下方缺节行为),不会静默跳过。
最小可运行路径
第一步:确认引用
Local 内置在基础包里,项目引用 Aegis.FileManager 即可(Aegis.FileManager.Nas / .S3 / .WebDav 等实现包都传递引用它,装了任一实现包也具备 Local 能力)。
dotnet add package Aegis.FileManager
第二步:准备配置
appsettings.json 添加 LocalStorage 节:
{
"LocalStorage": {
"RootPath": "/data/aegis-files"
}
}
第三步:在 Component.deps.json 中声明
{
"Components": {
"Load": [
"LocalFileManager"
]
}
}
Load 列表里还有其他组件时,把 LocalFileManager 加进同一数组即可。启动横幅出现「已注入服务 LocalFileManager」即注册成功。
也可以走 Startup 手动注册(两条路径二选一,不要同时用):
using Aegis.FileManager;
services.AddLocalFileManager(new LocalOptions { RootPath = "/data/aegis-files" });
第四步:在业务里使用
public class AttachmentService
{
private readonly IFileManager _fileManager;
public AttachmentService(IFileManager fileManager) => _fileManager = fileManager;
public Task<FileUploadResult> Upload(string fileName, Stream stream, string group = "")
=> _fileManager.UploadFileAsync(fileName, stream, group);
public Task<string> GetUri(string fileName, string group = "")
=> _fileManager.GetFileUriAsync(fileName, group);
}
完整的业务自拼 Key 模式与 EMR 病历示例,见 文件管理 — 核心概念:业务自拼 Key。
配置项说明(LocalOptions)
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
RootPath | string | "" | 必填,无默认值。存储根目录,所有文件操作都基于此路径。相对路径按进程工作目录解析,生产环境建议显式配置绝对路径 |
和 NasStorage:RootPath 的差异:NAS 的 RootPath 有默认值 "/rootPath",Local 刻意去掉了默认值。RootPath 为空时 deps 路径与手动路径都会报错,逼着部署者显式指定落盘位置,避免文件散落在意外的工作目录里。
行为细则
| 行为 | 说明 |
|---|---|
ContentMd5 | 不计算,GetFileInfoAsync 返回的 ContentMd5 为空。要 MD5 的场景用 NAS 后端(EnableMd5Calculation)或业务侧自行计算 |
groupName 含义 | RootPath 下的子目录,与 NAS 一致 |
GetFileUriAsync | 返回文件在本机的绝对路径(/ 分隔),不是可访问 URL |
DeleteFileAsync | 幂等,文件不存在返回 true(与 NAS / S3 / WebDAV 对齐) |
ListFilesAsync | 仅直接子文件(非递归),prefix 客户端过滤,continuationToken 为上一页最后一个文件名,与 NAS 同一套分页语义 |
GetFileAsync | filePath 目录 / 完整路径两种传法自动识别,规则见 文件管理 — GetFileAsync 的 filePath 参数约定 |
| 路径遍历防护 | LocalPathHelper.MapToLocalPath 与 NasPathHelper 同款防护:GetFullPath 解析后做带边界分隔符的根范围校验,逃逸出 RootPath 的路径抛 ArgumentException |
缺节行为
deps.json 声明了 LocalFileManager 但 appsettings 没有对应的 LocalStorage 节时,注册返回显式 Failure,启动横幅直接可见:
注入服务失败 LocalFileManager
Fail: Component.deps.json 已声明 LocalFileManager,但未找到 LocalStorage 配置节;请补齐 appsettings 配置或从 Load 列表移除该组件
处理方式横幅里已经写明:要么补配置节,要么从 Load 列表移除组件。
与其他后端共存:单注入守卫
IFileManager 在一个进程里只能有一个默认实现。已经注册过 IFileManager 时,第二个 AddXxxFileManager(Local / Nas / S3 / WebDAV 任一组合)抛 InvalidOperationException:
- deps.json 路径的冲突表现为横幅 Failure,先声明的组件生效
- Startup 手动路径的冲突直接中止启动
升级到 3.1.4-preview4 后若启动报「IFileManager 已注册」,检查 Startup 里是否残留了旧的手动注册行,删掉即可。详见 文件管理 — 单注入守卫。
诊断三件套
需要装配 诊断组件(Aegis.Diagnostics)(deps.json 声明 Diagnostics)。未装配时三件套是无人消费的惰性单例,零开销。
| 诊断能力 | 内容 |
|---|---|
| 健康探针 | 探针名「文件存储 · Local」。RootPath 下写 → 读 → 删探针文件 .aegis-healthprobe,5 秒超时;根目录缺失时自动创建并在结果里标注「根路径原不存在(已自动创建,请确认 RootPath 拼写)」。BlocksReady = true:存储不可用即摘流量。启动即巡检(dueTime 0)并周期复巡 |
| 诊断段 | /diag 页面「文件存储 · Local」段:类型、根路径 |
| 网络分层诊断 | /diag 段头「一键诊断 ›」触发(端点 ?diagnose=),Local 走路径三步:根路径存在性 → 可写探测(写删探针文件)→ 磁盘容量(总容量 / 可用容量) |
接入后怎么确认生效
按下面几项验收:
- 启动横幅「检查遗漏服务」不再出现「当前未加载 LocalFileManager」
IFileManager可以正常注入UploadFileAsync后,LocalStorage:RootPath下能看到对应分组目录与文件GetFileBytesAsync/DeleteFileAsync正常工作,删除后再DoesFileExistAsync返回 false- 装配了 Diagnostics 时,
/diag页面出现「文件存储 · Local」段,健康矩阵里该探针为绿
常见问题
Local 和 NAS 都是本地盘读写,怎么选
两者实现路线一样(System.IO 直写),差别在定位:
| 差异点 | Local | NAS |
|---|---|---|
| 所在包 | 基础包内置,无额外引用 | 独立包 Aegis.FileManager.Nas |
| MD5 | 不算,ContentMd5 为空 | EnableMd5Calculation 控制,默认算 |
RootPath 默认值 | 无默认,必填 | 默认 "/rootPath" |
| 典型场景 | 开发调试、单机、容器本地卷 | 生产挂载 NAS / 共享目录,需要 MD5 |
单机轻量用 Local;生产环境要 MD5、或有共享挂载诉求,用 NAS。
没配 RootPath 会怎样
- deps.json 路径:
LocalStorage节缺失 → 启动横幅 Failure(节存在但RootPath为空同样过不了校验,AddLocalFileManager抛ArgumentException,deps 路径转为横幅 Failure) - 手动路径:
AddLocalFileManager直接抛ArgumentException(LocalStorage:RootPath 不能为空)
启动横幅出现「当前未加载 LocalFileManager」是什么意思
「检查遗漏服务」段列出的是「程序集已引用、但没写进 Load 列表」的组件。业务确实用文件存储时,这条提示等于漏配:把 LocalFileManager(或你实际要用的后端)加进 deps.json,并补上对应配置节。判读细节见 文件管理 — 启动横幅判读指引。
配套阅读
- 文件管理(Aegis.FileManager) — 抽象层、组件化注册、后端选择
- NAS 本地存储(Aegis.FileManager.Nas) — 同路线的 NAS / 共享目录后端,支持 MD5
- 诊断组件(Aegis.Diagnostics) — 健康矩阵、
/diag页面与网络分层诊断