rxdb-plugin-storage
Implements: US-502 Storage 插件
基于 RxDB 与 Origin Private File System(OPFS)的浏览器文件存储插件:文件体写入 OPFS,文件元数据写入 RxDB。
能力范围
- 上传、覆盖、读取、删除和按目录清理文件
- 创建、列出和重命名目录
- 重命名文件
- 从远程 URL 拉取并永久缓存到 OPFS
- 通过
BlobURL 预览或下载文件 - 通过 RxDB 查询、监听文件元数据
- 对文件体与元数据的跨存储更新执行补偿回滚
以下能力尚未实现:
- 自定义
rxdb-file://协议或file://URL - stale-while-revalidate、ETag、TTL 或远程版本协商
- 缩略图生成和多级加载链路
- LRU 淘汰、流式分块上传和断点续传
- 跨设备文件体同步
数据模型
interface StorageFileMeta {
id: string;
name: string;
mimeType: string;
size: number;
opfsPath: string;
contentVersion: number;
createdAt: Date;
updatedAt: Date;
}
opfsPath是相对于插件rootDir的路径。contentVersion在覆盖写入或远程缓存更新时递增。- 文件名和目录名只允许单个合法路径段;
/、\\、空段、.、..和首尾空格会被拒绝,不会被静默改写。
公开 API
interface RxDBStoragePluginOptions {
rootDir?: string;
previewLimitBytes?: number;
}
interface UploadOptions {
path?: string;
overwrite?: boolean;
}
interface FetchRemoteOptions {
url: string;
mimeType?: string;
signal?: AbortSignal;
}
interface RenameOptions {
overwrite?: boolean;
}
class RxdbFileStorage {
readonly activeObjectUrlCount: number;
init(): Promise<void>;
upload(file: File, options?: UploadOptions): Promise<StorageFileMeta>;
read(fileId: string): Promise<Blob>;
fetch(opfsPath: string, options: FetchRemoteOptions): Promise<Blob>;
preview(fileId: string): Promise<StoragePreviewResult>;
createObjectUrl(fileId: string): Promise<string>;
revokeObjectUrl(url: string): void;
download(fileId: string, options?: { suggestedName?: string }): Promise<void>;
getMeta(fileId: string): Promise<StorageFileMeta | null>;
list(options?: { path?: string }): Promise<StorageFileMeta[]>;
listEntries(options?: { path?: string }): Promise<StorageBrowserEntry[]>;
watch(fileId: string): Observable<StorageFileMeta | null>;
createDirectory(name: string, options?: { path?: string }): Promise<string>;
rename(fileId: string, newName: string, options?: RenameOptions): Promise<StorageFileMeta>;
renameDirectory(directoryPath: string, newName: string, options?: RenameOptions): Promise<string>;
delete(fileId: string): Promise<void>;
clear(path?: string): Promise<void>;
destroy(): void;
}
list() 只返回当前目录直属文件的元数据;listEntries() 同时返回直属文件和子目录。
fetch() 的缓存键是规范化后的 opfsPath:
- 已有缓存时直接返回 OPFS 文件,不发网络请求。
- 同一路径并发请求共享一个 in-flight 请求。
- 非 2xx、离线、Abort 或 MIME 缺失不会污染现有缓存。
- 这是永久缓存,不包含自动刷新策略。
错误类型
StorageInvalidPathError;
StorageConflictError;
StorageUnavailableError;
StoragePreviewLimitError;
StorageOfflineError;
StorageFetchError;
StorageMimeTypeMissingError;
调用方应按错误类型处理,不要匹配英文错误消息。
当主操作失败且补偿回滚也失败时,服务会抛 AggregateError,其中第一个错误仍是原始失败原因。
使用方式
import { rxDBPluginStorage } from '@aiao/rxdb-plugin-storage';
rxdb.use(rxDBPluginStorage, {
rootDir: 'files',
previewLimitBytes: 50 * 1024 * 1024
});
await rxdb.storage.init();
const meta = await rxdb.storage.upload(file, {
path: '/avatars',
overwrite: true
});
const preview = await rxdb.storage.preview(meta.id);
try {
image.src = preview.url;
} finally {
preview.dispose();
}
远程缓存:
const blob = await rxdb.storage.fetch('images/avatar.png', {
url: 'https://static.example.com/avatar.png',
signal: abortController.signal
});
运行要求
rxdb.config.sync.local.adapter必须存在,元数据必须落到本地适配器。- 运行环境必须支持
navigator.storage.getDirectory()。 showSaveFilePicker可用时download()使用文件选择器;否则回退到临时<a download>。watch()监听本插件触发的元数据变化,不监视其他代码直接修改 OPFS 的行为。destroy()会回收本实例创建的全部对象 URL。
一致性策略
OPFS 与 RxDB 不共享事务。插件采用补偿日志维持用户可见一致性:
- 写入失败:恢复旧文件,或删除本次新建的孤儿文件。
- 元数据创建/更新失败:恢复对应文件体。
- 删除文件失败:重建已删除的元数据。
- 文件或目录重命名失败:按逆序恢复元数据、目标文件和本次新建目录。
这不是跨进程的分布式事务;同一路径仍应由一个应用实例串行修改。
开发命令
pnpm nx run rxdb-plugin-storage:typecheck
pnpm nx run rxdb-plugin-storage:lint --max-warnings=0
pnpm nx test rxdb-plugin-storage --run --browser.enabled=false --coverage.enabled=false
pnpm nx run rxdb-plugin-storage:test-browser --run --coverage.enabled=false
pnpm nx run rxdb-plugin-storage:build
Fileoverview
RxDB Storage Plugin 文件存储插件,基于 Origin Private File System (OPFS) 提供文件存储能力
主要功能:
- 文件上传下载(upload/download)
- 文件预览和对象 URL(preview/createObjectUrl)
- 文件和目录管理(list/createDirectory/rename/delete)
- 文件元数据管理(StorageFileMeta 实体)
Classes
| Class | Description |
|---|---|
| ObjectUrlRegistry | - |
| RxdbFileStorage | - |
| RxDBPluginStorage | RxDB 插件接口 |
| StorageConflictError | - |
| StorageFetchError | 远程请求成功送达但返回非 2xx 时抛出。 |
| StorageFileMeta | 实体基类装饰器配置 定义了所有实体共有的基础属性 |
| StorageInvalidPathError | - |
| StorageMimeTypeMissingError | HTTP 响应成功但既未传 options.mimeType 也无 Content-Type 响应头时抛出。 |
| StorageOfflineError | 远程资源不可达时抛出。 |
| StoragePreviewLimitError | - |
| StorageUnavailableError | - |
Interfaces
Type Aliases
| Type Alias | Description |
|---|---|
| StorageBrowserEntry | - |
Variables
| Variable | Description |
|---|---|
| rxDBPluginStorage | - |
| rxdbStorage | - |