簡介
文件頭是位於文件開頭的一段承擔一定任務的數據,一般都在開頭的部分。頭文件作為一種包含功能函數、數據接口聲明的載體文件,用於保存程序的聲明(declaration),而定義文件用於保存程序的實現 (implementation)。
為了解決在用戶上傳文件的時候在服務器端判斷文件類型的問題,故用獲取文件頭的方式,直接讀取文件的前幾個字節,來判斷上傳文件是否符合格式。
問題
現有一文件,其擴展名未知或標記錯誤。假設它是一個正常的、非空的文件,且將擴展名更正后可以正常使用,那么,如何判斷它是哪種類型的文件?
在后綴未知,或者后綴被修改的文件,依然通過文件頭來判斷該文件究竟是什么文件類型。我們可以使用一個文本編輯工具如 UltraEdit
、UniversalViewer
打開文件(16進制模式下),然后看文件頭是什么字符,以下是常見文件類型的文件頭字符(16進制),希望對你有幫助:
文件 | 文件頭 |
---|---|
JPEG (jpg) | FFD8FF |
PNG (png) | 89504E47 |
GIF (gif) | 47494638 |
TIFF (tif) | 49492A00 |
Windows Bitmap (bmp) | 424D |
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 |
Postscript (eps.or.ps) | 252150532D41646F6265 |
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 |
源碼
package com.blog.www.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/** * 文件類型 * <br/> * * @author :leigq * @date :2019/7/30 10:07 */
public class FileTypes {
private static final Map<FileTypeName, String> FILE_TYPE_MAP = new HashMap<>();
static {
// 初始化文件類型信息
initAllFileType();
}
private FileTypes() {
}
/** * 文件類型名稱枚舉 * <br/> * * @author :leigq * @date :2019/7/30 10:20 */
public enum FileTypeName {
JPG("jpg"),
PNG("png"),
GIF("gif"),
// ....根據自己需要添加更多
;
private String fileTypeName;
FileTypeName(String fileTypeName) {
this.fileTypeName = fileTypeName;
}
@Override
public String toString() {
return fileTypeName;
}
}
/** * 驗證文件類型 * <br/> * create by: leigq * <br/> * create time: 2019/7/30 10:25 * * @param fileTypeName 文件類型名稱枚舉 * @param file 文件 * @return if 文件類型 = 文件類型名稱枚舉 return true , else return false */
public static Boolean checkType(FileTypeName fileTypeName, File file) {
try {
FileInputStream fileInputStream = new FileInputStream(file);
byte[] b = new byte[10];
// 讀取文件前10個字節
int read = fileInputStream.read(b, 0, b.length);
if (read != -1) {
// 將字節轉換為16進制字符串
String fileCode = bytesToHexString(b).toUpperCase();
// 獲取對應文件類型的文件頭
String fileTypeHead = FILE_TYPE_MAP.get(fileTypeName);
return fileCode.startsWith(fileTypeHead) || fileTypeHead.startsWith(fileCode);
}
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
/** * 獲取文件類型 * * @param file 文件 * @return 文件類型,if 獲取不到類型 return "-1" */
public static String getType(File file) {
try {
FileInputStream is = new FileInputStream(file);
byte[] b = new byte[10];
int read = is.read(b, 0, b.length);
if (read != -1) {
String fileCode = bytesToHexString(b).toUpperCase();
for (Map.Entry<FileTypeName, String> next : FILE_TYPE_MAP.entrySet()) {
String fileTypeHead = next.getValue();
if (fileTypeHead.startsWith(fileCode) || fileCode.startsWith(fileTypeHead)) {
return next.getKey().toString();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
return "-1";
}
/** * 初始化常見文件頭信息 * <br/> * create by: leigq * <br/> * create time: 2019/7/30 10:19 */
private static void initAllFileType() {
// JPEG (jpg)
FILE_TYPE_MAP.put(FileTypeName.JPG, "FFD8FF");
// PNG (png)
FILE_TYPE_MAP.put(FileTypeName.PNG, "89504E47");
// GIF (gif)
FILE_TYPE_MAP.put(FileTypeName.GIF, "47494638");
// ....根據自己需要添加更多, 文件頭編碼請用大寫
}
/** * 將字節數組轉換成16進制字符串 * * @param bytes 待轉換字節數組 */
private static String bytesToHexString(byte[] bytes) {
StringBuilder stringBuilder = new StringBuilder();
for (byte b : bytes) {
int v = b & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}
}
測試
/** * 測試 * <br/> * create by: leigq * <br/> * create time: 2019/7/30 11:32 */
public static void main(String[] args) {
// 驗證 PNG
File pngImg = new File("C:\\file_type\\png_test.png");
System.out.println(checkType(FileTypeName.PNG, pngImg));
System.out.println(getType(pngImg));
System.out.println("------------------");
// 驗證 JPG
File jpgImg = new File("C:\\file_type\\jpg_test.jpg");
System.out.println(checkType(FileTypeName.JPG, jpgImg));
System.out.println(getType(jpgImg));
System.out.println("------------------");
// 驗證 GIF
File gifImg = new File("C:\\file_type\\gif_test.gif");
System.out.println(checkType(FileTypeName.GIF, gifImg));
System.out.println(getType(gifImg));
System.out.println("------------------");
// 驗證 未知類型文件
File unknown = new File("C:\\file_type\\123.md");
System.out.println(checkType(FileTypeName.GIF, unknown));
System.out.println(getType(unknown));
}
參考