通常我们会选择通过文件后缀来判断文件类型,对于一些类型被重命名的文件,就会判断失误。因此,通过文件头来进行判断会更准确些。

 
目录


 

常用的文件头

常用文件的文件头如下(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")
......

 
 
  如有不对,烦请指出,感谢~