Java依据文件头获取文件类型
简介
文件头是位于文件开头的一段承担一定任务的数据,一般都在开头的部分。头文件作为一种包含功能函数、数据接口声明的载体文件,用于保存程序的声明(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));
}
参考
作者:不敲代码的攻城狮
出处:https://www.cnblogs.com/leigq/
任何傻瓜都能写出计算机可以理解的代码。好的程序员能写出人能读懂的代码。