Exif

可交换图像文件格式(英语:Exchangeable image file format,官方简称Exif),是专门为数码相机的照片设定的文件格式,可以记录数码照片的属性信息和拍摄数据。

通过手机或者相机拍摄的照片中会携带Exif信息,而某些手机默认会携带地理位置等隐私信息,比如下面这张图片

IMG_20210125_191526

它的Exif信息为:

可以直接看到该照片使用Redmi K30 Pro拍摄于2021:01:25 19:15:28,甚至还可以直接看到经纬度信息(手机设置中可关闭)。这种未经过处理的照片直接上传到网上之后,可能会造成隐私的泄露。

Go读取Exif信息

通过包goexif/exif,可以查看照片的全部Exif信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func GetExif() error {
ImgFile := "./IMG_20210125_191526.jpg"
file, err := os.Open(ImgFile)
if err != nil {
fmt.Println(err)
return err
}
defer file.Close()
x, err := exif.Decode(file)
if err != nil {
return err
}
fmt.Printf("%+v", x)
return nil
}

因为没找到直接操作图片Exif的库(如果有欢迎留言告诉我),只能通过解析图片的色彩空间,然后通过此色彩空间重新生成一张新的图片因为是重新生成的图片,也就相当于抹除了Exif信息

1
2
3
4
5
6
7
8
9
10
11
12
13
func CreateNewImage(f, newF *os.File) error {
m, _, err := image.Decode(f)
if err != nil {
return err
}
// 获取宽高
width, height := m.Bounds().Max.X, m.Bounds().Max.Y
var subImage image.Image
img := m.(*image.YCbCr)
subImage = img.SubImage(image.Rect(0, 0, width, height)).(*image.YCbCr)
err = jpeg.Encode(newF, subImage, &jpeg.Options{100})
return err
}

所有代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package main

import (
"fmt"
"image"
_ "image/gif"
"image/jpeg"
_ "image/jpeg"
_ "image/png"
"os"
"path"
"strings"

"github.com/rwcarlsen/goexif/exif"
)

func main() {
ImgFile := "./IMG_20210125_191526.jpg"
fileNameWithSuffix := path.Base(ImgFile) // 获取文件名带后缀
imgType := path.Ext(fileNameWithSuffix) // 获取文件后缀
fileNameOnly := strings.TrimSuffix(fileNameWithSuffix, imgType) // 获取文件名
newImageFile := fileNameOnly + "_no_exif" + imgType
file, err := os.Open(ImgFile)
if err != nil {
fmt.Println("打开文件失败")
return
}
defer file.Close()
err = GetExif(file)
if err != nil {
panic(err)
}
// 设置文件偏移,重新从第一位开始读取
file.Seek(0, 0)
newFile, err := os.Create(newImageFile)
if err != nil {
fmt.Println("file create fail")
panic(err)
}
defer newFile.Close()
err = CreateNewImage(file, newFile)
if err != nil {
panic(err)
}
}

func GetExif(file *os.File) error {
x, err := exif.Decode(file)
if err != nil {
return err
}
fmt.Printf("%+v", x)

return nil
}

func CreateNewImage(f, newF *os.File) error {
m, _, err := image.Decode(f)
if err != nil {
return err
}
// 获取宽高
width, height := m.Bounds().Max.X, m.Bounds().Max.Y
var subImage image.Image
img := m.(*image.YCbCr)
subImage = img.SubImage(image.Rect(0, 0, width, height)).(*image.YCbCr)
err = jpeg.Encode(newF, subImage, &jpeg.Options{100})
return err
}

func Create(fileName string) (*os.File, error) {
f, err := os.Create(fileName) // 创建文件
if err != nil {
fmt.Println("file create fail")
return nil, err
}
return f, nil
}