【黑马程序员】Java如何判断文件的真实类型
在我们实现一些需求的时候,有时候需要让用户上传文件,例如做论坛、社交类的应用和服务。这时候可能就会出现一个需求:上传固定类型、固定大小的文件,也就是说,只允许用户上传图片或者文本、压缩包等。其他的格式禁止上传,以防止用户恶意操作。文件的大小非常容易判断,主要是文件类型如何确定。
有一种方式是按照扩展名去判断,例如图片就是.jpg、.png等,但是这种方式不准确,因为如果真是恶意上传,exe文件也可以修改为xxx.jpg上传成功。所以,必须要用一种方式获取文件的真实类型。即使文件后缀名修改了,也能出来它的原形。
这个地方我们需要先了解一些基本知识,就是所有的文件的开头都有一段头信息,用来承担一定的任务,而每个不同类型的文件的头信息都是不同的,所以我们可以按照这种方式来进行判断。
首先,我们需要获取到文件头里这几个字节的数据,很简单,使用输入流来读取即可。代码如下:
有一种方式是按照扩展名去判断,例如图片就是.jpg、.png等,但是这种方式不准确,因为如果真是恶意上传,exe文件也可以修改为xxx.jpg上传成功。所以,必须要用一种方式获取文件的真实类型。即使文件后缀名修改了,也能出来它的原形。
这个地方我们需要先了解一些基本知识,就是所有的文件的开头都有一段头信息,用来承担一定的任务,而每个不同类型的文件的头信息都是不同的,所以我们可以按照这种方式来进行判断。
首先,我们需要获取到文件头里这几个字节的数据,很简单,使用输入流来读取即可。代码如下:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 | public static String getFileHeader(String filePath) { FileInputStream is = null; String value = null; try { is = new FileInputStream(filePath); byte[] b = new byte[4]; is.read(b, 0, b.length); value = bytesToHexString(b); } catch (Exception e) { } finally { if (null != is) { try { is.close(); } catch (IOException e) { } } } return value; } |
这个地方很容易理解,就是把文件的前4个byte取出来了,但是取出来的数据我们需要转换为16进制的字符串来表示,才能判断出来究竟是什么内容。接下来,我们就把数据转换一下。Integer里有一个API可以直接输出16进制的字符串,所以我们可以先把byte转换为integer来表示。由于byte是一个字节,而integer是4个字节,所以不足的部分用0填充,即用byte与0xFF做一次与运算,完成byte到integer的转换,然后把16进制的数据输出,字符串转换为大写以便后面使用。
01 02 03 04 05 06 07 08 09 10 培训机构出来的程序员真的强吗11 12 13 14 15 16 | private static String bytesToHexString(byte[] src) { StringBuilder builder = new StringBuilder(); if (src == null || src.length <= 0) { return null; } String hv; for (int i = 0; i < src.length; i++) { // 以十六进制(基数 16)无符号整数形式返回一个整数参数的字符串表示形式,并转换为大写 hv = HexString(src[i] & 0xFF).toUpperCase(); if (hv.length() < 2) { builder.append(0); } builder.append(hv); } return String(); } |
得到了头信息里的数据之后,我们拿到的其实是一堆看上去像乱码的字符串,并不能判断出来究竟是什么类型的文件,所以我们需要搜集常见的文件类型的头信息都是什么,然后用映射关系表来查。我这里准备了一些常用的文件的头信息,已经封装到了一个map里,可以参考。
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | public static final HashMap<String, String> mFileTypes = new HashMap<String, String>(); static { // images mFileTypes.put("FFD8FF", "jpg"); mFileTypes.put("89504E47", "png"); mFileTypes.put("47494638", "gif"); mFileTypes.put("49492A00", "tif"); mFileTypes.put("424D", "bmp"); // mFileTypes.put("41433130", "dwg"); // CAD mFileTypes.put("38425053", "psd"); mFileTypes.put("7B5C727466", "rtf"); // 日记本 mFileTypes.put("3C3F786D6C", "xml"); mFileTypes.put("68746D6C3E", "html"); mFileTypes.put("44656C69766572792D646174653A", "eml"); // 邮件 mFileTypes.put("D0CF11E0", "doc"); mFileTypes.put("D0CF11E0", "xls");// excel2003版本文件 mFileTypes.put("5374616E64617264204A", "mdb"); mFileTypes.put("252150532D41646F6265", "ps"); mFileTypes.put("255044462D312E", "pdf"); mFileTypes.put("504B0304", "docx"); mFileTypes.put("504B0304", "xlsx");// excel2007以上版本文件 mFileTypes.put("52617221", "rar"); mFileTypes.put("57415645", "wav"); mFileTypes.put("41564920", "avi"); mFileTypes.put("2E524D46", "rm"); mFileTypes.put("000001BA", "mpg"); mFileTypes.put("000001B3", "mpg"); mFileTypes.put("6D6F6F76", "mov"); mFileTypes.put("3026B2758E66CF11", "asf"); mFileTypes.put("4D546864", "mid"); mFileTypes.put("1F8B08", "gz"); } |
这些都是一些固定的数据,我们现在有了map的key(其实就是上面我们最终获取到的字符串),直接去map里查value就是文件的实际类型了。我们把一个png文件修改为1.avi试一下。打印结果是OK的。
这样,我们就可以把上面的代码整理封装为一个工具类来使用了,以后再处理文件上传的时候,就可以把一些恶意的操作屏蔽掉了。整体代码附到附件里自取吧。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论