본문으로 건너뛰기

파일 및 이미지

사용자 파일은 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. 캐시 정리는 원본을 건드리지 않습니다.

원자적 쓰기

모든 파일 쓰기는 임시 파일 후 이름 변경(rename) 방식으로 이루어집니다. 중간에 크래시가 나도 부분적으로 기록된 파일이 남지 않습니다. 이는 원본, 썸네일, 다운로드된 토크나이저 파일 모두에 적용됩니다.

캐릭터 갤러리

갤러리 이미지는 owner_type = character.galleryattachments 테이블을 재사용합니다. 메타데이터 행은 원본과 썸네일의 URL을 보유하고, 바이트는 콘텐츠 주소 지정된 files/avatars/에 남습니다. 갤러리에서 이미지를 제거하면 첨부 레코드만 삭제되고 원본 파일은 삭제되지 않습니다. 작업은 되돌릴 수 있고 중복 제거는 유지됩니다.

채팅 배경

files/backgrounds/가 진실의 원천입니다. 목록은 디렉터리 스캔으로 만들어지므로 SillyTavern에서 가져온 배경도 전송 단계 없이 나타납니다. 업로드된 파일은 콘텐츠 주소로 저장되고 절대 수정되지 않습니다.

배경 썸네일은 콘텐츠가 아닌 파일 이름의 SHA-256으로 키가 지정된 cache/thumbnails/에 있습니다. 덕분에 임의 이름의 SillyTavern 가져오기 파일도 썸네일을 얻고, 업로드·목록·삭제가 하나의 키에서 이루어집니다. 디코딩할 수 없거나 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/ 루트는 유지되므로 활성 마이그레이션 스테이징 디렉터리가 중단되지 않습니다. 결과는 제거된 파일의 수와 크기를 보고하며, 재실행은 안전하고 0을 반환합니다.