跳到主要内容

文件和图像

用户文件存储在磁盘上,绝不会作为 BLOB:原始文件位于 data/files/, 可重新生成的缩略图位于 data/cache/thumbnails/,每次写入都是原子的。

原始文件与缓存

这种分离是严格的:

  • 原始文件 —— data/files/{avatars,backgrounds,attachments,audio,generated}/。 原始文件绝不会被修改,也绝不会被缓存维护删除。
  • 缓存 —— data/cache/thumbnails/。缩略图是可重新生成的,并且是 内容寻址的。

清除缓存绝不会移除原始文件。缺失的缩略图会自动从原始文件重新生成。

图像导入管线

导入图像遵循一个固定的管线:

  1. 校验大小、MIME 类型和扩展名。
  2. 计算内容哈希(SHA-256)。
  3. 无损保存原始文件,内容寻址({sha256}{ext}),按内容去重。
  4. 为画廊、列表和预览生成低分辨率缩略图。
  5. 把缩略图存储在 data/cache/thumbnails/
  6. 用原始文件的哈希、目标大小和算法版本作为每个缩略图的键: {hash}-{size}-v{algorithmVersion}
  7. 键未变化的缩略图不重新生成。
  8. 缩略图足够的地方绝不加载原始文件。
  9. 缩略图缺失时自动重建缓存。
  10. 缓存清除绝不触碰原始文件。

原子写入

每次文件写入都经过临时文件加重命名。中途崩溃绝不会留下写了一半的文件。 这同样适用于原始文件、缩略图和下载的分词器文件。

角色画廊

画廊图像复用 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/ 根目录被 保留,因此活动的迁移暂存目录绝不会被中断。结果报告移除文件的数量和大小; 重新运行它是安全的,并返回零。