os.stat() 返回 os.fileinfo,含 name()、size()、mode()、modtime() 四个核心字段,适用于判断存在性、大小、权限、修改时间等系统级元数据,不提供 exif 等内容层信息。

os.Stat() 能拿到哪些元数据?够用吗?
绝大多数场景下,os.Stat() 就是你要的答案——它返回 os.FileInfo,包含 Name()、Size()、Mode()、ModTime() 四个核心字段,不打开文件、不读内容、系统调用开销极小。
常见误判是以为它“不够全”:它确实不提供 EXIF、作者、编码、页数这些业务级元数据,但对判断文件是否存在、大小是否合理、是否可读、最后修改时间是否有效,完全够用且最快。
-
Size()是精确逻辑字节数,不是磁盘占用(ZFS/Btrfs/NTFS 压缩卷上会明显偏大) -
Mode()是os.FileMode类型,别直接打印数值;用fi.Mode().IsDir()或fi.Mode().Perm()提取语义 -
ModTime()精度依赖文件系统:ext4 是纳秒,FAT32 是 2 秒,跨平台比较时务必加 ±1s 容忍 - 路径是符号链接时,
os.Stat()返回目标文件信息;要链接自身属性,改用os.Lstat()
需要 EXIF、GPS、PDF 属性?必须用 go-exiftool
Go 标准库不解析文件内容层元数据。go-exiftool 是目前最稳的方案,它调用系统已安装的 exiftool 二进制,支持图片、PDF、Office 文档等上百种格式。
注意它不是纯 Go 实现,依赖外部工具,所以部署时得确保 exiftool 在 $PATH 中(Debian/Ubuntu:sudo apt-get install exiftool)。
- 初始化后建议复用
exiftool.Exiftool实例,避免反复 fork 进程 -
et.ExtractMetadata("file.jpg")返回结构体切片,每个元素含Fields map[string]interface{}和Err - 字段名是 ExifTool 默认键(如
"DateTimeOriginal"、"GPSLatitude"),不是驼峰或下划线统一格式,需查文档或先打印 keys - 大文件批量处理时,启用
exiftool.StayOpen模式(库默认开启),否则每文件启动一次进程,性能暴跌
遍历目录时别用 filepath.Walk,改用 os.ReadDir
如果你要快速扫描一个目录下所有文件的名称、大小、类型,os.ReadDir() 比 filepath.Walk() 快 2–5 倍,因为它只发一次系统调用读整个目录项,且默认不触发 stat。
关键点在于:返回的 fs.DirEntry 对象,Name() 和 IsDir() 是零成本的,但 Info() 才真正调 stat——别无脑调用 entry.Info().Size(),否则就失去性能优势了。
- 只需过滤文件名或类型?直接用
entry.Name()和entry.IsDir() - 需要大小或修改时间?再对符合条件的条目调
entry.Info() - 想跳过符号链接目标?
os.ReadDir不自动跟随,行为更可控 - 它不递归,真要深度遍历仍得自己写栈或队列,但单层快得多
提取日志/文本中的结构化字段?别建 Trie,用 offset + 关键词索引
从大文本文件(如 10GB 日志)里快速定位某关键词(如 "user_12345" 或 "ERROR")的行,不要把整行塞进前缀树——Go 里内存爆炸、GC 崩溃、构建慢。
真实高效的做法是分离存储:Trie 只存关键词本身,每个关键词挂一组字节偏移量,靠 file.Seek() 定位原文。
- 用
bufio.Scanner流式读,每行用正则或strings.SplitN提取关键词 - 在
scanner.Bytes()后、scanner.Scan()前调file.Seek(0, io.SeekCurrent)获取当前偏移(scanner.Text()后指针已跳,不准) - Trie 叶子节点存
[]int64,不是map[string][]int64——省掉字符串哈希和拷贝 - 关键词重复率高(如 80% 是
"INFO"),对 offset 列表做 delta 编码 +binary.PutUvarint,能压掉 60%+ 内存
真正容易被忽略的是:稀疏文件、/proc/kcore、/dev/zero 这类特殊文件,Size() 极大但实际读不到那么多字节,流式处理时得配限长或超时,不能只信 Size()。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











