通常我们会选择通过文件后缀来判断文件类型,对于一些类型被重命名的文件,就会判断失误。因此,通过文件头来进行判断会更准确些。
目录
常用的文件头
常用文件的文件头如下(16进制):
JPEG (jpg),文件头:FFD8FFE0或FFD8FFE1或FFD8FFE8
GIF (gif),文件头:47494638PNG (png),文件头:89504E47
TIFF (tif),文件头:49492A00
Windows Bitmap (bmp),文件头:424DC001
CAD (dwg),文件头:41433130
Adobe Photoshop (psd),文件头:38425053
Rich Text Format (rtf),文件头:7B5C727466
XML (xml),文件头:3C3F786D6C
HTML (html),文件头:68746D6C3E
Email [thorough only] (eml),文件头:44656C69766572792D646174653A
Outlook Express (dbx),文件头:CFAD12FEC5FD746F
Outlook (pst),文件头:2142444E
MS Word/Excel (xls.or.doc),文件头:D0CF11E0
MS Access (mdb),文件头:5374616E64617264204A
WordPerfect (wpd),文件头:FF575043
Adobe Acrobat (pdf),文件头:255044462D312E
Quicken (qdf),文件头:AC9EBD8F
Windows Password (pwl),文件头:E3828596
ZIP Archive (zip),文件头:504B0304
RAR Archive (rar),文件头:52617221
Wave (wav),文件头:57415645
AVI (avi),文件头:41564920
Real Audio (ram),文件头:2E7261FD
Real Media (rm),文件头:2E524D46
MPEG (mpg),文件头:000001BA
MPEG (mpg),文件头:000001B3
Quicktime (mov),文件头:6D6F6F76
Windows Media (asf),文件头:3026B2758E66CF11
MIDI (mid),文件头:4D546864
go代码示例
只判断了是否是jpg/jpeg/png,如需判断/获取其他类型,可以自行添加map
package main
import (
"bytes"
"encoding/hex"
"fmt"
"log"
"os"
"strconv"
"strings"
)
var picMap map[string]string
func init() {
picMap = make(map[string]string)
picMap["ffd8ffe0"] = "jpg"
picMap["ffd8ffe1"] = "jpg"
picMap["ffd8ffe8"] = "jpg"
picMap["89504e47"] = "png"
}
func main() {
file,err := os.Open("pic/type/test.jpeg")
if err != nil {
log.Fatal(err.Error())
}
defer file.Close()
result := judgeType(file)
fmt.Println("判断结果: ",result)
}
func judgeType(file *os.File) bool {
buf := make([]byte, 20)
n, _ := file.Read(buf)
fileCode := bytesToHexString(buf[:n])
for k,_ := range picMap {
if strings.HasPrefix(fileCode, k) {
return true
}
}
return false
}
//获取16进制
func bytesToHexString(src []byte) string {
res := bytes.Buffer{}
if src == nil || len(src) <= 0 {
return ""
}
temp := make([]byte, 0)
i, length := 100, len(src)
if length < i {
i = length
}
for j := 0; j < i; j++ {
sub := src[j] & 0xFF
hv := hex.EncodeToString(append(temp, sub))
if len(hv) < 2 {
res.WriteString(strconv.FormatInt(int64(0), 10))
}
res.WriteString(hv)
}
return res.String()
}
一些小坑
在读取文件时,调用Read()、ReadAll()等方法,会清空缓存区,如果后续还需要用到完整文件的话,会导致文件不完整。
如果可以,先从文件流 []byte中拷贝一份前20字节进行判断,或者读取完文件后再重新写入。
对于(gin.Context、fasthttp等)请求过来的文件,可以先用对请求进行拷贝,这样不会破坏源数据。
var c *gin.Context
copyC := c.Cppy()
fileHeader, err := copyC.FormFile("file_name")
......
如有不对,烦请指出,感谢~