文件版本识别必须结合sha256哈希与modtime,仅依赖modtime不可靠;缓存需保存data、hash、modtime三者;http缓存应以etag(哈希值)驱动,设cache-control为public, max-age=31536000。

文件内容变更时,仅靠 os.Stat().ModTime 不可靠,必须结合哈希校验才能准确识别真实版本变化。
用 SHA256 哈希作为文件唯一版本标识
文件是否“变了”,不能只看修改时间——编辑器保存、Git checkout、NFS挂载都可能触发时间戳更新但内容未变。SHA256 是轻量且抗碰撞的校验方式,适合做内容级版本锚点。
- 每次读取文件后立即计算
sha256.Sum256(data),转成十六进制字符串作为版本 ID - 避免对大文件全量读取再哈希:可分块读取 +
hash.Hash.Write()流式计算 - 哈希值应参与缓存键构造,例如
cacheKey := filepath.Base(path) + "_" + hash,防止同名不同内容命中旧缓存 - 不推荐 MD5:虽快但已不安全,且 Go 标准库中
crypto/md5无常量时间比较支持,易受定时攻击(哪怕只是内部校验)
内存缓存需同时保存内容、哈希与 ModTime
单纯缓存 []byte 不够——下次请求时无法判断是否需重载。必须把三者打包为结构体,否则会漏掉“文件被覆盖但大小/时间未变”的边界情况(比如 cp --preserve=timestamps)。
- 定义
CachedFile结构体,字段包括Data、Hash、ModTime - 使用
sync.Map而非普通map,避免并发读写 panic;key 用绝对路径(filepath.Abs),避免软链或相对路径导致重复缓存 - 首次加载后,后续请求先
os.Stat拿当前ModTime,与缓存中对比;若相同,直接返回缓存数据;若不同,才重新读取+哈希+更新 - 注意:Windows 下 FAT32 文件系统 ModTime 精度只有 2 秒,
time.Time.Equal可能误判,建议用Before/After加容差比较
Cache-Control 头应由哈希值而非时间驱动
HTTP 缓存头如果只依赖 ModTime,浏览器或 CDN 就无法长期缓存——只要文件被 touch 过,max-age 就得重算。而哈希不变,说明内容真没变,就该复用一年。
- 响应中设置
Cache-Control: public, max-age=31536000(1 年),并附上ETag: "<hash>"</hash> - 客户端发
If-None-Match时,直接比对 ETag 字符串,无需再读文件 - 不要用
Last-Modified做主缓存依据:它和 ModTime 绑定,而 ModTime 不等于内容变更 - 静态资源 URL 中嵌入哈希(如
/static/app.js?v=abc123)是客户端缓存兜底手段,服务端仍需正确返回Cache-Control
真正难的是冷启动后的首次哈希计算和并发加载竞争——多个 goroutine 同时发现缓存缺失,可能重复读同一文件。加锁或用 sync.Once 包裹加载逻辑,比事后删重更省资源。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











