用os.stat()获取文件修改时间、大小和类型最直接,返回fileinfo接口提供modtime()、size()和mode()方法;注意不提供mime类型,需额外读文件头判断,符号链接应使用os.lstat()避免跟随。

如何用 Go 获取文件的修改时间、大小和类型
Go 标准库 os.Stat() 是最直接的方式,它返回 os.FileInfo 接口,包含 ModTime()、Size() 和 Mode()。注意:它不提供 MIME 类型(比如 image/jpeg),这部分需额外读取文件头判断。
常见错误是只调用 os.Stat() 就认为“拿到了所有元数据”,结果缓存里缺 MIME 类型,后续按类型分发逻辑出错。
- 对普通文件,
os.Stat()足够;对符号链接,加os.Lstat()避免跟随 - MIME 类型必须靠前几个字节识别,推荐用
net/http.DetectContentType(),但只读前 512 字节(不是整个文件) -
FileInfo.Mode().IsDir()和FileInfo.Mode().IsRegular()比字符串比较更可靠
用 Go map 或 sync.Map 做内存缓存合适吗
简单场景用 map[string]FileMeta + sync.RWMutex 更可控;高并发读多写少时,sync.Map 看似省事,但它不支持遍历、不支持原子更新结构体字段,反而容易踩坑。
例如你想更新某个文件的 ModTime,sync.Map 只能整条替换,而 map + RWMutex 可以加锁后只改字段——这对避免缓存击穿有实际意义。
- 缓存 key 建议用绝对路径(
filepath.Abs()),避免相对路径导致重复存入 - 不要把
time.Time直接当 map key(Go 不支持),转成字符串或 Unix 纳秒值 - 如果需要过期机制,别自己轮询删 key,用
github.com/patrickmn/go-cache这类成熟库更稳
为什么 Stat 结果不能直接 JSON 序列化
os.FileInfo 是接口,json.Marshal() 对它序列化会得到空对象 {},因为底层结构体字段都是非导出(小写开头)。
必须定义自己的结构体,显式复制字段:
type FileMeta struct {
Path string `json:"path"`
Size int64 `json:"size"`
ModTime int64 `json:"mod_time"` // 存 Unix 时间戳,而非 time.Time
IsDir bool `json:"is_dir"`
MIMEType string `json:"mime_type,omitempty"`
}
-
ModTime.Unix()或ModTime.UnixNano()才能进 JSON;保留time.Time会导致 marshal 失败或输出不可预测格式 - 如果缓存要跨进程(比如存 Redis),MIMEType 必须提前算好塞进去,不能每次从文件重读
- 注意 Windows 路径中的
\在 JSON 里会被转义,不影响解析,但日志里看着乱,可统一转为/
缓存失效怎么跟文件系统变化联动
Go 没有内置 inotify,Linux 下可用 fsnotify 库监听目录变更,但要注意:它不保证事件顺序,也不通知文件内容是否真变了(比如只是 touch 更新时间)。
更稳妥的做法是「懒校验」:缓存中存 ModTime,每次读缓存前先 os.Stat() 比对时间戳,不一致再重新提取并更新缓存。
- 不要依赖
fsnotify的Write事件就立刻刷新——可能写了一半,Stat()还拿不到最终大小 - 大文件频繁变动时,建议加个最小间隔(如 100ms)去抖,避免反复触发
- 容器环境或 NFS 卷下,
fsnotify可能完全不工作,此时只能降级为定时轮询
路径、权限、硬链接数这些字段一旦缓存,就很难靠监听自动维护,得接受“缓存比实际滞后”的事实。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











