Java 读取图片文件的类型(MimeType)

原创
2018/01/21 20:51
阅读数 4.5W

一、问题描述

在项目开发的时候,我们经常会遇到一类文件上传的问题,就是获取图片是哪种格式。很多情况下,很多人都是用后缀名去判断,如下所示。

if(filename.endsWith(".png") || filename.endsWith(".jpg"))
{
   //保存图片

}else{
   throw new IOException("Error file format !");

}

但是这种方式相当不可靠,我们可以尝试将zip文件、rmvb文件、css、js修改后缀名位jpg或者png上传,也可以上传到服务器,这就造成我们服务器上出现了脏数据。此外,对于有些图片文件,修改成错误的扩展名,有些浏览器可能无法显示出此图片。

 

二、解决方案

在计算机系统中,媒体类型(MimeType的文件都有【标识符】,zip、图片本身属于媒体文件,因此我们可以通过编解码的方式判断图片是否合法。

1、判断标示方法

private static boolean isBMP(byte[] buf){
		byte[] markBuf = "BM".getBytes();  //BMP图片文件的前两个字节
		return compare(buf, markBuf);
	}
	
	private static boolean isICON(byte[] buf) {
		byte[] markBuf = {0, 0, 1, 0, 1, 0, 32, 32};
		return compare(buf, markBuf);
	}
	private static boolean isWEBP(byte[] buf) {
		byte[] markBuf = "RIFF".getBytes(); //WebP图片识别符
		return compare(buf, markBuf);
	}

	private static boolean isGIF(byte[] buf) {
		
		byte[] markBuf = "GIF89a".getBytes(); //GIF识别符
		if(compare(buf, markBuf))
		{
			return true;
		}
		markBuf = "GIF87a".getBytes(); //GIF识别符
		if(compare(buf, markBuf))
		{
			return true;
		}
		return false;
	}

	
	private static boolean isPNG(byte[] buf) {
		
		byte[] markBuf = {(byte) 0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A}; //PNG识别符
		 // new String(buf).indexOf("PNG")>0 //也可以使用这种方式
		return compare(buf, markBuf);
	}

	private static boolean isJPEGHeader(byte[] buf) {
		byte[] markBuf = {(byte) 0xff, (byte) 0xd8}; //JPEG开始符
		
		return compare(buf, markBuf);
	}
	
	private static boolean isJPEGFooter(byte[] buf)//JPEG结束符
	{
		byte[] markBuf = {(byte) 0xff, (byte) 0xd9}; 
		return compare(buf, markBuf);
	}

2、核心方法

/**
	 * 获取文件的mimeType
	 * @param filename
	 * @return
	 */
	private static String getMimeType(String filename){
		try {
			String mimeType = readType(filename);
			return String.format("image/%s", mimeType);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 读取文件类型
	 * @param filename
	 * @return
	 * @throws IOException
	 */
	private static String readType(String filename) throws IOException {
		
		FileInputStream fis = null;
		try {
			File f = new File(filename);
			if(!f.exists() || f.isDirectory() || f.length()<8) {
				throw new IOException("the file ["+f.getAbsolutePath()+"] is not image !");
			}
			
			fis= new FileInputStream(f);
			byte[] bufHeaders = readInputStreamAt(fis,0,8);
			if(isJPEGHeader(bufHeaders))
			{	
				long skiplength = f.length()-2-8; //第一次读取时已经读了8个byte,因此需要减掉
				byte[] bufFooters = readInputStreamAt(fis, skiplength, 2);
				if(isJPEGFooter(bufFooters))
				{
					return "jpeg";
				}
			}
			if(isPNG(bufHeaders))
			{
				return "png";
			}
			if(isGIF(bufHeaders)){
				
				return "gif";
			}
			if(isWEBP(bufHeaders))
			{
				return "webp";
			}
			if(isBMP(bufHeaders))
			{
				return "bmp";
			}
			if(isICON(bufHeaders))
			{
				return "ico";
			}
			throw new IOException("the image's format is unkown!");
			
		} catch (FileNotFoundException e) {
			throw e;
		}finally{
			try {
				if(fis!=null) fis.close();
			} catch (Exception e) {
			}
		}
		
	}

	
	/**
	 * 标示一致性比较
	 * @param buf  待检测标示
	 * @param markBuf 标识符字节数组
	 * @return 返回false标示标示不匹配
	 */
	private static boolean compare(byte[] buf, byte[] markBuf) {
		for (int i = 0; i < markBuf.length; i++) {
			byte b = markBuf[i];
			byte a = buf[i];
			
			if(a!=b){
				return false;
			}
		}
		return true;
	}
	/**
	 * 
	 * @param fis 输入流对象
	 * @param skiplength 跳过位置长度
	 * @param length 要读取的长度
	 * @return 字节数组
	 * @throws IOException
	 */
	private static byte[] readInputStreamAt(FileInputStream fis, long skiplength, int length) throws IOException
	{
		byte[] buf = new byte[length];
		fis.skip(skiplength);  //
		int read = fis.read(buf,0,length);
		return buf;
	}
	

3、测试代码

正常测试

public class ImageType {

	
	public static void main(String[] args) {
		
			String filename = "oschina.jpg";
			String type = getMimeType(filename);
			System.out.println(type);
	}
}

输出

image/jpeg

修改扩展名测试

①修改oschina.jpeg为oschina.png

②复制oschina.png删除扩展名

public class ImageType {

	
	public static void main(String[] args) {
		
			String filename = "oschina.png";
			String type = getMimeType(filename);
			System.out.println(type);
		
			filename = "oschina";
			type = getMimeType(filename);
			System.out.println(type);
            
	}
}

输出

image/jpeg
image/jpeg

 

展开阅读全文
打赏
3
17 收藏
分享
加载中
IamOkay博主

引用来自“freezingsky”的评论

文件类型判断 一般都是通过解析文件头来完成的。 目前已经有相应的的工具可以操作,试一下:jmimemagic。
好的,谢谢!
2018/01/22 17:27
回复
举报
文件类型判断 一般都是通过解析文件头来完成的。 目前已经有相应的的工具可以操作,试一下:jmimemagic。
2018/01/22 17:20
回复
举报
更多评论
打赏
2 评论
17 收藏
3
分享
返回顶部
顶部