Java 通过魔数判断上传文件的类型

前言

文件上传功能是很多网站都必须的功能,而判断文件类型不仅可以过滤文件的上传,同时也能防范用户上传恶意的可执行文件和脚本,以及将文件上传服务器当作免费的文件存储服务器使用。

而对于上传文件来说,不能简单的通过后缀名来判断文件的类型,因为恶意攻击可以将可执行文件的后缀名称改为图片或者其他格式,诱导用户执行,因此,判断上传文件的类型需要更安全的方式。

与Java的class文件类似,很多类型的文件,起始的几个字节内容都是固定的,跟据这几个字节的内容,就可以判断文件的类型,这几个字节也被称为“魔数”,比如class文件的魔数就是“CAFEBABE”。

通过魔数判断文件类型便是一种更安全的方式,其示例源码如下。

源码

1.新建一个文件类型的枚举类


public enum FileType {
	
	/** JPEG */
	JPEG("FFD8FF"),
	
	/** PNG */
	PNG("89504E47"),
	
	/** GIF */
	GIF("47494638"),
	
	/** TIFF */
	TIFF("49492A00"),
	
	/** Windows bitmap */
	BMP("424D"),
	
	/** CAD */
	DWG("41433130"),
	
	/** Adobe photoshop */
	PSD("38425053"),
	
	/** Rich Text Format */
	RTF("7B5C727466"),
	
	/** XML */
	XML("3C3F786D6C"),
	
	/** HTML */
	HTML("68746D6C3E"),
	
	/** Outlook Express */
	DBX("CFAD12FEC5FD746F "),
	
	/** Outlook */
	PST("2142444E"),
	
	/** doc;xls;dot;ppt;xla;ppa;pps;pot;msi;sdw;db */
	OLE2("0xD0CF11E0A1B11AE1"),
	
	/** Microsoft Word/Excel */
	XLS_DOC("D0CF11E0"),
	
	/** Microsoft Access */
	MDB("5374616E64617264204A"),
	
	/** Word Perfect */
	WPB("FF575043"),
	
	/** Postscript */
	EPS_PS("252150532D41646F6265"),
	
	/** Adobe Acrobat */
	PDF("255044462D312E"),
	
	/** Windows Password */
	PWL("E3828596"),
	
	/** ZIP Archive */
	ZIP("504B0304"),
	
	/** ARAR Archive */
	RAR("52617221"),
	
	/** WAVE */
	WAV("57415645"),
	
	/** AVI */
	AVI("41564920"),
	
	/** Real Audio */
	RAM("2E7261FD"),
	
	/** Real Media */
	RM("2E524D46"),
	
	/** Quicktime */
	MOV("6D6F6F76"),
	
	/** Windows Media */
	ASF("3026B2758E66CF11"),
	
	/** MIDI */
	MID("4D546864");
	
	private String value = "";

	private FileType(String value) {
		this.value = value;
	}
	
	public String getValue() {
		return value;
	}

	public void setValue(String value) {
		this.value = value;
	}
	
}

2.新建一个文件工具类,用来判断上传文件的类型

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;


public class FilUtil {
	
	/** 判断文件类型  */
	public static FileType getType(String filePath) throws IOException {
		// 获取文件头
		String fileHead = getFileHeader(filePath);
		
		if (fileHead != null && fileHead.length() > 0) {
			fileHead = fileHead.toUpperCase();
			FileType[] fileTypes = FileType.values();
			
			for (FileType type : fileTypes) {
				if (fileHead.startsWith(type.getValue())) {
					return type;
				}
			}
		}
		
		return null;
	}
	
	/** 读取文件头 */
	private static String getFileHeader(String filePath) throws IOException {
		byte[] b = new byte[28];
		InputStream inputStream = null;
		
		try {
			inputStream = new FileInputStream(filePath);
			inputStream.read(b, 0, 28);
		} finally {
			if (inputStream != null) {
				inputStream.close();
			}
		}
		
		return bytesToHex(b);
	}
	
	/** 将字节数组转换成16进制字符串 */
	public static String bytesToHex(byte[] src){  
	    StringBuilder stringBuilder = new StringBuilder("");  
	    if (src == null || src.length <= 0) {  
	        return null;  
	    }
	    for (int i = 0; i < src.length; i++) {  
	        int v = src[i] & 0xFF;  
	        String hv = Integer.toHexString(v);  
	        if (hv.length() < 2) {  
	            stringBuilder.append(0);  
	        }  
	        stringBuilder.append(hv);  
	    }  
	    return stringBuilder.toString();  
	} 
	
}

以上,需要注意的是,可能存在一种类型文件的魔数与另一种类型文件魔数的前面部分相等(如‘D0CF11E0’与‘D0CF11E0A1B11AE1’),所以应该尽量将更长的魔数值放在上面。这里的文件与魔数枚举得也不全面,但思路就这样吧。

参考文献
[1]陈康贤.大型分布式网站架构设计与实践[M].北京:电子工业出版社.2014.09

Java中,可以通过以下方法来判断上传文件类型: 1. 通过文件名后缀名判断 可以通过文件名中的后缀名来判断文件类型,例如: ```java String fileName = "example.jpg"; if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg")) { // 文件类型为jpg或jpeg } else if (fileName.endsWith(".png")) { // 文件类型为png } else { // 其他文件类型 } ``` 但需要注意的是,文件名后缀可以被篡改,因此此方法不是十分可靠。 2. 通过文件的MIME类型判断 可以通过文件的MIME类型判断文件类型,例如: ```java import javax.activation.MimetypesFileTypeMap; MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap(); String mimeType = mimeTypesMap.getContentType(filePath); if (mimeType.startsWith("image/")) { // 文件类型为图片 } else if (mimeType.startsWith("video/")) { // 文件类型为视频 } else if (mimeType.startsWith("text/")) { // 文件类型为文本文件 } else { // 其他文件类型 } ``` 需要注意的是,MIME类型也可以被篡改。 3. 通过文件内容判断 可以通过读取文件的内容来判断文件类型,例如: ```java import java.io.*; public static String getFileType(String filePath) throws IOException { InputStream inputStream = new FileInputStream(filePath); byte[] bytes = new byte[4]; inputStream.read(bytes); inputStream.close(); String type = ""; String fileHead = bytesToHexString(bytes); if (fileHead == null || fileHead.length() == 0) { return null; } fileHead = fileHead.toUpperCase(); FileType[] fileTypes = FileType.values(); for (FileType fileType : fileTypes) { if (fileHead.startsWith(fileType.getValue())) { type = fileType.name(); break; } } return type; } public static String bytesToHexString(byte[] bytes) { StringBuilder stringBuilder = new StringBuilder(); if (bytes == null || bytes.length <= 0) { return null; } for (int i = 0; i < bytes.length; i++) { int v = bytes[i] & 0xFF; String hv = Integer.toHexString(v); if (hv.length() < 2) { stringBuilder.append(0); } stringBuilder.append(hv); } return stringBuilder.toString(); } enum FileType { JPEG("FFD8FF"), // JPEG (jpg) PNG("89504E47"), // PNG (png) GIF("47494638"), // GIF (gif) TIFF("49492A00"), // TIFF (tif) BMP("424D"), // Windows Bitmap (bmp) DWG("41433130"), // CAD (dwg) PSD("38425053"), // Adobe Photoshop (psd) RTF("7B5C727466"), // Rich Text Format (rtf) XML("3C3F786D6C"), // XML (xml) HTML("68746D6C3E"), // HTML (html) PDF("255044462D312E"), // Adobe PDF (pdf) ZIP("504B0304"), // ZIP Archive (zip) RAR("52617221"), // RAR Archive (rar) WAV("57415645"), // Wave (wav) AVI("41564920"), // AVI (avi) MP4("00000020667479706D70"), // MPEG-4 (mp4) MPG("000001BA"), // MPEG (mpg) WMV("3026B2758E66CF11"), // Windows Media Video (wmv) MID("4D546864"); // MIDI (mid) private String value = ""; private FileType(String value) { this.value = value; } public String getValue() { return value; } } ``` 此方法可以通过读取文件的前几个字节来判断文件类型,比较可靠,但需要注意的是,不同类型文件前几个字节可能是相同的,因此有一定的误判率。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值