最稳妥方案是使用 github.com/rwcarlsen/goexif/exif:需 os.open 文件后调用 exif.decode;字段用 exif.datetime 等常量获取;datetime 需转 time.local;gps 用 gpsmap.latlong() 解析;报 invalid tiff header 时先验文件格式与完整性。

用 github.com/rwcarlsen/goexif/exif 读取基础 EXIF 字段最稳妥
Go 原生不支持 EXIF 解析,必须依赖第三方库。目前最成熟、维护活跃、兼容性好的是 rwcarlsen/goexif(注意:不是已归档的 goexif 原始库)。它能正确处理 JPEG 和 TIFF 封装的 EXIF,对常见相机、手机拍摄图兼容性好。
安装命令:go get github.com/rwcarlsen/goexif/exif
关键点:
- 必须先用
os.Open打开文件,再用exif.Decode解析 —— 它不接受 raw bytes 切片,也不接受*os.File以外的io.Reader(比如bytes.NewReader会因 seek 不支持而失败) - EXIF 数据通常在 JPEG 的 APP1 段中,该库会自动定位;但若图片被裁剪/重编码过,APP1 段可能丢失,此时
exif.Decode返回nil, nil,需做空值判断 - 字段名使用标准 IFD 标签常量,如
exif.DateTime、exif.Make、exif.Model,不是字符串键
获取 DateTime 时要注意时区和格式解析
exif.DateTime 字段返回的是 *exif.TiffTime 类型,底层是 time.Time,但它的时区信息来自 EXIF 中的 “DateTimeOriginal” 字段原始字符串(如 "2023:05:12 14:30:22"),**不包含时区偏移**。这意味着它默认按本地时间解析,且 Go 的 time.Parse 无法直接识别这种格式。
正确做法是手动解析字符串并补上本地时区:
if date, err := exifTag.Get(exif.DateTime, nil); err == nil {
if t, ok := date.(*exif.TiffTime); ok {
// t.Time 是已解析的 time.Time,但时区为 UTC(库内部硬设)
// 实际应按系统本地时区理解,所以需重新以 Local 时区解释
local := t.Time.In(time.Local)
fmt.Println("拍摄时间(本地时区):", local.Format("2006-01-02 15:04:05"))
}
}
更安全的方式是直接读 DateTimeOriginal 字符串字段并自行 parse:
- 用
exifTag.StringVal()获取原始字符串 - 用
time.Parse("2006:01:02 15:04:05", s)解析(注意年月日间是英文冒号) - 再调用
.In(time.Local)转为本地时间(除非你明确需要 UTC)
读取 GPS 坐标需要额外处理有理数和方向标识
GPS 信息(如 exif.GPSLatitude、exif.GPSLongitude)在 EXIF 中以有理数(Rat)数组形式存储,且附带方向标签(GPSLatitudeRef = "N" 或 "S")。
不能直接用 Get() 取值后类型断言为 float64 —— 它返回的是 []*exif.Rat(三个分数:度、分、秒)。
推荐用库内置的辅助函数:
if gps, err := exifTag.Get(exif.GPSInfo, nil); err == nil {
if gpsMap, ok := gps.(*exif.GPSInfo); ok {
lat, _ := gpsMap.LatLong()
fmt.Printf("纬度: %.6f, 经度: %.6f\n", lat[0], lat[1])
}
}
注意:LatLong() 内部已处理方向标识和有理数转浮点,但前提是 GPSInfo 字段存在且完整(部分手机图可能只存了 GPSLatitude 而缺 GPSLatitudeRef,此时会返回 0)。
遇到 exif: invalid TIFF header 错误别急着换库
这个错误绝大多数情况不是库的问题,而是传入了非 JPEG/TIFF 文件,或文件开头被截断(比如 HTTP multipart 上传时没读全 body)、或图片是 WebP/HEIC 格式(它们不带传统 EXIF,而是用 XMP 或私有块)。
排查步骤:
- 用
file -i your.jpg确认 MIME 类型确实是image/jpeg - 检查文件头是否为
0xFF 0xD8(JPEG SOI marker) - 确认打开文件后未被其他 goroutine 并发读写导致 offset 错乱
- WebP/HEIC 图需改用
golang.org/x/image/webp或专用库(如github.com/evanoberholster/imagemeta),它们支持从 XMP 提取元数据
EXIF 解析本身不耗 CPU,但 IO 是瓶颈;如果批量处理,建议复用 *exif.Exif 实例(通过 exif.DecodeWithExif),避免重复解析相同结构。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











