生命周期与查重
有效期的三层取值与访问续期、到期清理与三段式删除、冷图待清理的打标与裁决,以及精确重复自动合并与相似重复人工裁决。
有效期(TTL)
每张图可以有一个有效期,到期后自动删除。有效期按三层取值,从上往下第一个有定义的生效:
| 层级 | 在哪设置 | 取值 |
|---|---|---|
| 单次上传 | 表单字段 ttl、URL 转存的 JSON 字段 ttl、后台上传弹窗的「有效期」 | 秒数;0 或 never 表示永久 |
| 用户默认 | 「用户」页 → 编辑 →「默认有效期(小时)」 | 留空表示随全局默认;0 表示永久;正数为小时数 |
| 全局默认 | 「设置 → 运行配置」的「默认有效期」default_ttl_hours | 0 表示永久(出厂值);正数为小时数 |
- 单次上传显式写
0或never,会覆盖用户默认与全局默认,这张图就是永久的。 - S3 上传不能逐次指定,只按用户默认与全局默认取值。
- 到期时刻在上传时算定并保存。之后再改用户默认或全局默认,不影响已有的图。
- 要表达「临时」,给图设一个短有效期即可;没有单独的临时目录或临时上传类型。
- 单次上传、用户默认与全局默认的上限都是 876000 小时(100 年),超出即拒绝。其余换算成时长的运营配置(扫描间隔、删除宽限、保留期、超时等)按各自单位折算,同样以 100 年为上限。
已有图片的有效期在图片详情里修改(图片的归属用户与管理员都可以):
- 「修改有效期」→ 填天数后点「延长」:到期时刻改为从现在起算的 N 天后,这个时长也成为该图新的有效期(访问续期按它顺延)。
- 「设为永久」:去掉到期时刻。
访问即续期
「访问即续期」ttl_renew_on_access 出厂开启。开启时,带有效期的图每被公开访问一次,到期时刻就顺延为「这次访问被记录的时刻 + 它的有效期」。
效果是有效期变成「连续这么久没人访问才过期」:有人在用的图一直保留,没人看的图到期清理。关闭后,到期时刻固定不变。
- 访问在内存里累计,每
activity_flush_seconds(出厂 60 秒)批量写入一次,续期在写入时发生。写入那一刻已经到期的图不再续期:到期是终态。 - 只有
/i/与/o/的公开访问算数;后台缩略图与预览、S3 GetObject 不算,见 后台浏览不计访问。 - 永久的图不受影响。
到期清理
清理扫描在进程启动时先跑一轮,之后每 cleanup_sweep_seconds(出厂 60 秒)一轮,每轮依次:
- 删除已到期的图(每轮最多 500 张,积压的下一轮继续);
- 物理删除已过宽限期的文件(见下节);
- 给冷图打待清理标记(见 冷图待清理);
- 回收超过保留期的上传幂等键;
- 设置了保留天数时,回收过期的审计日志(
audit_retention_days,出厂 0 表示永久保留)。
图一到期,对外链接就返回 404,也不会再被访问续期;记录与文件由之后的清理扫描回收。在那之前,后台「图片」页里它显示为「已过期」,仍可手动删除。进程停机期间到期的图,在重启后的扫描里补删。
删除流程
所有删除都走同一条流程:到期、后台删除(单张或批量,一次最多 100 张)、待清理裁决的删除、S3 DeleteObject。
删除引用
在一个事务里删除这条图片记录(连同指向它的幂等键),并把它所指物理文件的引用计数减 1。链接立即返回 404。
配置了 Cloudflare 缓存清理时,同时为 /i/<短ID> 及这张图生成过的衍生图参数组合排队清理 CDN 缓存;/o/ 别名链接不在清理范围内。见 CDN。
标记待删
引用计数降到 0 时,物理文件被标记为待删除,删除时刻 = 现在 + 删除宽限 delete_grace_minutes(出厂 0,即下一轮扫描就删)。
物理删除
删除时刻过后的那一轮扫描里:先删掉它的衍生图缓存,再在事务内复查它确实无人引用后删除数据库记录,然后为每个持有副本的渠道排队删除对象。 渠道删除异步执行、失败重试;最终没删掉的对象成为孤儿,由对账任务回收。
引用计数:一个物理文件可以被多条图片记录共享(查重合并的结果)。删除其中一条不影响其他记录,只有最后一条引用删除后物理文件才会进入删除流程。图片详情里的「引用数」显示当前值。
后台的删除确认框会按当前宽限期提示物理文件大约多久后清理。删除用户之前必须先删掉该用户名下的全部图片。
冷图待清理
长时间没人访问的图会被打上「待清理」标记,进入后台「待清理」页等管理员裁决。不会自动删除。 删除不可撤销,所以机器只负责发现,删不删由人决定;相似重复的合并也是同一原则。
- 阈值是「冷图清理阈值」
cold_cleanup_days,出厂 90 天;设为 0 不再打标。 - 打标:每轮清理扫描时,最近一次公开访问(从未访问过则取上传时间)早于阈值、且未被豁免的图,打上标记。
- 摘标:
- 这张图被公开访问(在下一次访问计数写入时摘掉);
- 管理员裁决「保留」;
- 管理员取消该图的清理豁免(下一轮扫描重新判定);
- 这张图被删除。
- 调大阈值或把阈值设为 0,不会摘掉已有的标记。
- 冷图判定与有效期互不相关:带有效期的图同样可能被标记,被豁免的图照样会到期。
裁决
「待清理」页(仅管理员)按最近访问时间从早到晚列出,可单张或批量操作:
- 「保留」:给图设上清理豁免,之后不再参与冷图打标,有效期与访问续期不受影响。可在图片详情里取消豁免。
- 「删除」:走上面的删除流程。
清理豁免只有管理员能设置,与冷图裁决同属管理员的权限,归属用户无法自己给图打上豁免、绕开回收:图片详情里的「清理豁免」开关可以在图变冷之前预先打开;普通用户看得到状态但不能修改。 「图片」页的筛选「仅看已豁免」列出全部已豁免的图。
查重
查重不在上传路径上进行,上传不会因此变慢。后台查重循环在进程启动时先跑一轮,之后每 dedup_scan_seconds(出厂 300 秒)一轮,每轮先合并精确重复,再扫描相似重复。
精确重复:自动合并
实际保存的字节完全相同(SHA-256 相同)的物理文件,合并没有任何风险,自动进行:
- 保留其中最早的一份,其余文件的图片记录改为指向它,引用计数并入;
- 多出来的文件标记待删,按删除宽限期物理删除;
- 跨用户同样合并。每条图片记录仍保留自己的短 ID、归属用户、有效期与访问计数,所有链接继续可用;
- 配额按引用计全额,合并不减少任何用户的已用量。
相似重复:人工裁决
每个物理文件上传时都算了 64 位感知哈希(pHash)。两个文件的哈希差异位数(汉明距离)不超过「相似判定阈值」dedup_phash_max_distance(出厂 10)时,作为一对候选进入「疑似重复」页。
- 阈值越大越宽松,误报越多;设为 0 不做相似扫描。后台提示距离 0–2 基本是同一张图。
- 距离为 0 也只进候选、不自动合并:感知哈希相同不代表字节相同。
- 扫描是增量的:每轮只拿新文件与已有文件比较,在内存里逐一计算距离,自部署的规模不需要索引结构;进程重启后会全量重扫一次,已忽略的候选不会再出现。
裁决与选择保留哪一张
「疑似重复」页(仅管理员)把每对候选并排显示为左图、右图,附格式、尺寸、大小、引用数与距离,按距离从小到大排列:
- 「保留这张」→ 确认「合并」:另一张的全部引用改为指向保留的这张,被合并的文件按宽限期清理,涉及它的其他候选一并移除。不可撤销。
- 「忽略」:这一对以后不再提示,可批量忽略。
合并后,被合并一方的所有链接(包括其他用户的引用)开始输出保留方的内容:尺寸、格式、画面可能都不同。所以通常保留分辨率更高、格式更合适的一张;两张实为不同图片时选「忽略」。 已经缓存了旧内容的浏览器与 CDN 在缓存过期前仍显示旧图。
合并与忽略都记入审计日志(动作「合并重复图」「忽略重复图」)。
相关配置
| 配置键 | 后台名称 | 出厂值 |
|---|---|---|
default_ttl_hours | 默认有效期 | 0(永久) |
ttl_renew_on_access | 访问即续期 | 开 |
cold_cleanup_days | 冷图清理阈值 | 90 天 |
delete_grace_minutes | 删除宽限 | 0 分钟 |
cleanup_sweep_seconds | 清理扫描间隔 | 60 秒 |
activity_flush_seconds | 活跃度落库间隔 | 60 秒 |
idempotency_key_ttl_hours | 幂等键保留期 | 24 小时 |
dedup_scan_seconds | 查重扫描间隔 | 300 秒 |
dedup_phash_max_distance | 相似判定阈值 | 10 |
audit_retention_days | 审计日志保留天数 | 0(永久) |
都在「设置 → 运行配置」修改,保存即生效,见 配置。