ファイルと画像
ユーザーファイルは 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}。 - キーが変わっていないサムネイルは再生成しません。
- サムネイルで足りるところにオリジナルを読み込みません。
- サムネイルが欠けているときはキャッシュを自動的に再構築します。
- キャッシュのクリアはオリジナルに触れません。
アトミック書き込み
すべてのファイル書き込みは一時ファイルの後にリネームを行います。途中でクラッシュしても、部分的に書き込まれたファイルが残ることはありません。これはオリジナル、サムネイル、ダウンロードされたトークナイザーファイルのすべてに適用されます。
キャラクターギャラリー
ギャラリー画像は owner_type = character.gallery で attachments テーブルを再利用します。メタデータ行はオリジナルとそのサムネイルの URL を保持し、バイトはコンテンツアドレス型の files/avatars/ に残ります。ギャラリーから画像を削除すると添付レコードが削除されますが、オリジナルファイルは削除されません — 操作は元に戻せ、重複排除は維持されます。
チャット背景
files/backgrounds/ が情報源です: 一覧はディレクトリのスキャンで構築されるため、SillyTavern からインポートされた背景は転送ステップなしで表示されます。アップロードされたファイルはコンテンツアドレス型で保存され、変更されることはありません。
背景サムネイルは cache/thumbnails/ にあり、コンテンツではなくファイル名の SHA-256 でキー付けされます。これにより、任意の名前を持つ SillyTavern インポートファイルもサムネイルを得られ、アップロード・一覧・削除が 1 つのキーに保たれます。デコードできないファイルや 64 MiB を超えるファイルはサムネイルなしで一覧表示され、オリジナルは利用可能なままです。背景を削除するとオリジナルとキャッシュされたサムネイルの両方が削除されます。
キャラクターカードのインポート
POST /api/v2/characters/import は JSON Character Card 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/ ルートは保持されるため、アクティブなマイグレーションステージングディレクトリが中断されることはありません。結果は削除されたファイルの数とサイズを報告し、再実行は安全でゼロを返します。