关于上传文件类型验证的解决办法
【转载:http://www.blogjava.net/bolo/archive/2012/05/01/376804.html】
大象根据研究与实际项目经验,向大家介绍一个关于文件类型验证的解决办法。不清楚的朋友可以了解下,知道的不喜勿喷。
对于文件上传,相信大家都不会陌生,我们都知道,文件在上传到服务器的过程中,都是以流的形式传输的,在后台处理文件上传的代码中,获得这个流,然后读取数据流将之保存到上传文件的临时目录中,如果有使用到MongoDB,再将这个文件存储到文件系统中。
大部分的文件上传都是通过HTML的上传组件完成的,而业务需求往往是有类型要求的,比如只允许上传jpg、gif、png类型的图片,或者是只允许上传Office文档等等,虽然可以用JavaScript对上传文件做一些类型验证之类的控制,但还是不能完全做到过虑。这时,就需要在后台,用代码来进一步完成这个验证工作。
到底通过什么方式可以做到正确验证呢?答案就是通过文件的头部信息,通过大量测试,大象发现每种类型的文件,他们最开始的一段信息都是一样的,比如Office97~03,它的头四位16进制信息就是d0 cf 11 e0,而Office2007则是50 4b 03 04,PDF为25 50 44 46,大家可以多用这样的文件分别测试一下,看看前四位16进制信息是不是都是一样的。当然这其中也有个别情况,比如jpg类型的图片,它的前四位16进制信息就有两种一个是ff d8 ff e0,另一个是ff d8 ff e1,区别是最后一位。知道了这些,我们就有一个方向了。
可能有同学有疑问了,为什么只取前四位,不是六位或八位呢?这是因为,大象根据反复测试发现,从第五位开始到第八位,同一种类型的文件,在这几位里面很有一些存在区别,像图片以及pdf,这种现象很多,为了避免同一类型的文件,因为这一些小的不同,要定义N多检测头信息,这样做似乎没有必要,因此大象才建议取前四位作为类型检测的依据。
不过说了这么多,还是没讲怎么做,这显然不是大象的风格,大象一般都从实际出发,用代码来说话。
package com.bolo.util;public class FileValidateUtil { public static boolean validateType(byte[] b, String customTypes) { if (b != null) { int size = b.length; String hex = null; StringBuilder contentType = new StringBuilder(); for (int i = 0; i < size; i++) { hex = Integer.toHexString(b[i] & 0xFF); if (hex.length() == 1) { hex = "0" + hex; } contentType.append(hex); if (i > 2) break; } if (customTypes.indexOf(contentType.toString()) > -1) { return Boolean.TRUE; } } return Boolean.FALSE; }}
package com.bolo.util;import java.io.IOException;import junit.framework.Assert;import org.apache.commons.io.FileUtils;import org.junit.Test;import org.springframework.util.ResourceUtils;import com.bolo.util.FileValidateUtil;public class FileValidateUtilTest { /** * 文件头部信息,十六进制信息,取前4位 * 50 4b 03 04 office 2007+ * d0 cf 11 e0 office 97~03 * 25 50 44 46 pdf * ff d8 ff e0 jpg,部分png与jpg头文件前4位一样 * ff d8 ff e1 jpg,一种不同的jpg头文件 * 89 50 4e 47 png */ private static final String FILE_TYPE = "504b0304 d0cf11e0 25504446 ffd8ffe0 ffd8ffe1 89504e47"; @Test public void jpgTest(){ validateType("file/1.jpg"); } @Test public void docTest(){ validateType("file/2.doc"); } @Test public void docxTest(){ validateType("file/3.docx"); } @Test public void pdfTest(){ validateType("file/4.pdf"); } @Test public void exeTest(){ validateType("file/5.png"); } private void validateType(String path){ try { Assert.assertTrue(FileValidateUtil.validateType(FileUtils .readFileToByteArray(ResourceUtils.getFile("classpath:" + path)), FILE_TYPE)); } catch (IOException e) { e.printStackTrace(); } }}