文件和图像
用户文件存储在磁盘上,绝不会作为 BLOB:原始文件位于 data/files/,
可重新生成的缩略图位于 data/cache/thumbnails/,每次写入都是原子的。
原始文件与缓存
这种分离是严格的:
- 原始文件 ——
data/files/{avatars,backgrounds,attachments,audio,generated}/。 原始文件绝不会被修改,也绝不会被缓存维护删除。 - 缓存 ——
data/cache/thumbnails/。缩略图是可重新生成的,并且是 内容寻址的。
清除缓存绝不会移除原始文件。缺失的缩略图会自动从原始文件重新生成。
图像导入管线
导入图像遵循一个固定的管线:
- 校验大小、MIME 类型和扩展名。
- 计算内容哈希(SHA-256)。
- 无损保存原始文件,内容寻址(
{sha256}{ext}),按内容去重。 - 为画廊、列表和预览生成低分辨率缩略图。
- 把缩略图存储在
data/cache/thumbnails/。 - 用原始文件的哈希、目标大小和算法版本作为每个缩略图的键:
{hash}-{size}-v{algorithmVersion}。 - 键未变化的缩略图不重新生成。
- 缩略图足够的地方绝不加载原始文件。
- 缩略图缺失时自动重建缓存。
- 缓存清除绝不触碰原始文件。
原子写入
每次文件写入都经过临时文件加重命名。中途崩溃绝不会留下写了一半的文件。 这同样适用于原始文件、缩略图和下载的分词器文件。
角色画廊
画廊图像复用 attachments 表,owner_type = character.gallery。
元数据行持有原始文件和其缩略图的 URL;字节保留在内容寻址的
files/avatars/ 中。从画廊移除图像会删除附件记录,而不是原始文件 ——
该操作保持可逆,去重得以保留。
聊天背景
files/backgrounds/ 是事实来源:列表通过扫描目录构建,
因此从 SillyTavern 导入的背景无需任何传输步骤即可出现。上传的文件以
内容寻址方式存储,绝不会被修改。
背景缩略图位于 cache/thumbnails/,键是文件名的 SHA-256 而不是内容的
SHA-256,这让名称任意的 SillyTavern 导入文件也能获得缩略图,
并让上传、列表和删除保持在同一个键上。无法解码或超过 64 MiB 的文件
在列表中不显示缩略图;原始文件仍然可用。删除背景会同时移除原始文件和
其缓存的缩略图。
角色卡导入
POST /api/v2/characters/import 接受 JSON 角色卡 V1/V2 和带 chara
元数据的 PNG。输入限制为 25 MiB,并按内容检测。整个源文件的 SHA-256
存储在 ext._st2.importHash 中,重新导入同一个文件会返回现有记录。
PNG 通过图像解码器验证。原始文件原子地写入 files/avatars/,
并生成 WebP 缩略图;缺失的缩略图会在下次读取时从原始文件重建。
缓存维护
诊断屏幕调用 DELETE /api/v2/diagnostics/cache,它只移除
cache/thumbnails/ 中的文件及其 cache_metadata 行。cache/ 根目录被
保留,因此活动的迁移暂存目录绝不会被中断。结果报告移除文件的数量和大小;
重新运行它是安全的,并返回零。