微笑.jpg,这里年轻人们聊天时经常使用的说法,但是!作为一个严谨的程序员,我们能不能通过文件的后缀来判断这个文件的类型呢?例如看到电脑上存在“微笑.jpg”这个文件,通过后缀 jpg
来判断它就是一个 JPEG
文件呢,答案肯定是否定的,因为文件名可以被轻松篡改,例如这个文件本来是“微笑.gif”,有人想不开就将它改成“微笑.jpg”,虽然没有什么影响,但是可以说明一个问题,我们不能通过文件后缀来判断文件的类型。
如果不使用文件后缀,我们应该如何判断文件的类型呢,答案就是我们今天要讨论的Magic Number
,即魔术数,因为文件的本质就是字节数组,所以在一般的操作系统中,对于特定类型的文件,其开头的几个字节都是相同的,例如对于 JPEG
格式的文件,开头的字节都是 \xff\xd8\xff
,我们可以通过读取文件开头的字节来确定文件的类型,这种方法准确性更高。
下面我们准备一个魔术数到类型的字典,注意,这里只列举了几个例子,还可以按照这样的格式增加字典项。
1 | var magicNumber = map[string]string { |
真正匹配格式的逻辑比较简单,遍历 magicNumber
这个 map
,判断每个 key
是不是某个字符串的前缀,因为魔术数符合前缀编码的特点,即任何一个编码都不是另一个编码的前缀,所以我们能保证判断文件类型时,不会有二义。
1 | func main() { |
gate upload
下面我们来看一个 Magic Number
的应用场景,例如我们有一个图片上传程序,但是只能允许上传 JPEG
格式的文件,给定一个 io.Reader
, 我们先读取前两个字节,判断其值是否等于 JPEG
的 Magic Number
,如果不等,则直接停止上传,如果相等,则继续上传。由于此时 r
已经读取了两个字节了,所以需要重新构造一个 io.Reader
( r := io.MultiReader(bytes.NewReader(b), r)
)继续上传。利用 Magic Number
我们可以避免将整个文件读入内存中,增加程序的运行效率。
1 | func handleUpload(r io.Reader) error { |