关于上传文件类型验证的解决办法

    大象根据研究与实际项目经验,向大家介绍一个关于文件类型验证的解决办法。不清楚的朋友可以了解下,知道的不喜勿喷。
    对于文件上传,相信大家都不会陌生,我们都知道,文件在上传到服务器的过程中,都是以流的形式传输的,在后台处理文件上传的代码中,获得这个流,然后读取数据流将之保存到上传文件的临时目录中,如果有使用到MongoDB,再将这个文件存储到文件系统中。
    大部分的文件上传都是通过HTML的上传组件完成的,而业务需求往往是有类型要求的,比如只允许上传jpg、gif、png类型的图片,或者是只允许上传Office文档等等,虽然可以用JavaScript对上传文件做一些类型验证之类的控制,但还是不能完全做到过虑。这时,就需要在后台,用代码来进一步完成这个验证工作。
    到底通过什么方式可以做到正确验证呢?答案就是通过文件的头部信息,通过大量测试,大象发现每种类型的文件,他们最开始的一段信息都是一样的,比如Office97~03,它的头四位16进制信息就是d0 cf 11 e0,而Office2007则是50 4b 03 04PDF25 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;

    }

}
 

    上面这段代码就是用来对文件类型作验证的方法,第一个参数是文件的字节数组,第二个就是定义的可通过类型。代码很简单,主要是注意中间的一处,将字节数组的前四位转换成16进制字符串,并且转换的时候,要先和0xFF做一次与运算。这是因为,整个文件流的字节数组中,有很多是负数,进行了与运算后,可以将前面的符号位都去掉,这样转换成的16进制字符串最多保留两位,如果是正数又小于10,那么转换后只有一位,需要在前面补0,这样做的目的是方便比较,取完前四位这个循环就可以终止了。
    下面我们准备些文件来测试一下这段代码有没有问题。

 

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,部分pngjpg头文件前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();

        }

    }

}

    从测试代码可以看到文件类型,只有第五个不是正确文件,是我将一个exe文件通过改后缀为png。这个测试类,用到了commons-io的FileUtils,需要在pom中加入这个依赖,而ResourceUtils在org.springframework.util包中,它属于spring-core.jar,大象这个测试类是放在之前ssm3工程test里面,要想正常运行测试,要在test/resources目录下建个file文件夹存放测试文件,最后编译之后file及其测试文件都会在test-classes下面,所以文件查找是以classpath:开头。
     OK,运行测试,结果就是前四个成功,最后一个失败,这达到了我们的预期,只允许FILE_TYPE里面定义的文件类型通过测试。大家可以自己动手试验一下。
    本文为菠萝大象原创,如要转载请注明出处。http://bolo.blogjava.net/ 

 

自己总结:

jpg的头:FF D8 FF E0 00

gif的头:47 49 46 38 39

png的头:89 50 4E 47 0D

InputStream photoFile=new FileInputStream(photo); 
				byte[] data =readInputStream(photoFile);
				String s = bytes2HexString(data);
				 if(!("ffd8").equals(s.substring(0, 3))){//jpg格式
					 errorMsg = "请上传JPG格式的近期照片!";
						return showPhoto();
			    }





	 private  byte[] readInputStream(InputStream inStream) throws Exception{  
	       ByteArrayOutputStream outStream = new ByteArrayOutputStream();  
		       //创建一个Buffer字符串  
		        byte[] buffer = new byte[1024];  
		        //每次读取的字符串长度,如果为-1,代表全部读取完毕  
		        int len = 0;  
		       //使用一个输入流从buffer里把数据读取出来  
		      while( (len=inStream.read(buffer)) != -1 ){  
		            //用输出流往buffer里写入数据,中间参数代表从哪个位置开始读,len代表读取的长度  
		            outStream.write(buffer, 0, len);  
		        }  
		        //关闭输入流  
		        inStream.close();  
		        //把outStream里的数据写入内存  
		        return outStream.toByteArray();  
		    }  
	
	
	
	  private  String bytes2HexString(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();

	    }


 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
什么是SWFUpload?   SWFUpload是一个客户端文件上传工具,最初由Vinterwebb.se开发,它通过整合Flash与JavaScript技术为WEB开发者提供了一个具有丰富功能继而超越传统标签的文件上传模式。 [编辑本段]SWFUpload的主要特点   * 可以同时上传多个文件;   * 类似AJAX的无刷新上传;   * 可以显示上传进度;   * 良好的浏览器兼容性;   * 兼容其他JavaScript库 (例如:jQuery, Prototype等);   * 支持Flash 8和Flash 9;   SWFUpload不同于其他基于Flash构建的上传工具,它有着优雅的代码设计,开发者可以利用XHTML、CSS和JavaScript来随心所欲的定制它在浏览器下的外观;它还提供了一组简明的JavaScript事件,借助它们开发者可以方便的在文件上传过程中更新页面内容来营造各种动态效果。   在使用SWFUpload之前,请确认你具备一定的JavaScript和DOM知识。在实际开发中,大部分的错误都是由于错误的设置和低劣的Event Handlers处理程序所造成的。 [编辑本段]文档中文翻译   http://www.v-sky.com/doc/swfupload/v2.1.0/Documentation.html [编辑本段]效果演示   * Classic Form Demo http://demo.swfupload.org/formsdemo ;   * Features Demo http://demo.swfupload.org/featuresdemo ;   * Application Demo http://demo.swfupload.org/applicationdemo ;   * v1.0.2 Plugin Demo http://demo.swfupload.org/v102demo ; [编辑本段]选择合适的Flash控件   在发行包(SWFUpload v2)中含有2个版本的Flash控件(swfupload_f8.swf 与wfupload_f9.swf),其中第一个版本拥有最佳的兼容性,但是为此损失了部分功能;而第二个版本提供了一些附加的功能但是损失了兼容性。 [编辑本段]SWFUpload的初始化与配置   首先,在页面中引用SWFUpload.js ,如      然后,初始化SWFUpload ,如   var swfu;   window.onload = function () {   swfu = new SWFUpload({   upload_url : "http://www.swfupload.org/upload.php",   flash_url : "http://www.swfupload.org/swfupload_f9.swf", file_size_limit : "20480"   });   };   以下是一个标准的SWFUpload初始化设置所需的参数,你可以根据需要自己进行删减:   {   upload_url : "http://www.swfupload.org/upload.php", 处理上传请求的服务器端脚本URL   file_post_name : "Filedata", 是POST过去的$_FILES的数组名   post_params : {   "post_param_name_1" : "post_param_value_1",   "post_param_name_2" : "post_param_value_2",   "post_param_name_n" : "post_param_value_n"   },   file_types : "*.jpg;*.gif", 允许上传的文件类型   file_types_description: "Web Image Files", 文件类型描述   file_size_limit : "1024", 上传文件体积上限,单位MB   file_upload_limit : 10, 限定用户一次性最多上传多少个文件,在上传过程中,该数字会累加,如果设置为“0”,则表示没有限制   file_queue_limit : 2, 上传队列数量限制,该项通常不需设置,会根据file_upload_limit自动赋值   fl
Javascript表单验证控件(Validator v1.05).rar<br>---------------------------------<br>内含以下两个文件:<br>Validator.chm(详细的使用帮助文档)<br>validator.js(源代码,当然没有prototype.js强大,但最重要的是功能非常实用,使用非常简单。)<br>---------------------------------<br>(如果有需要,我会另外把prototype的相关代码及使用开发手册也发上来)<br>---------------------------------<br>具体简介如下:<br>表单的验证一直是网页设计者头痛的问题,表单验证类 Validator就是为解决这个问题而写的,旨在使设计者从纷繁复杂的表单验证中解放出来,把精力集中于网页的设计和功能上的改进上。 <br> Validator是基于JavaScript技术的伪静态类和对象的自定义属性,可以对网页中的表单项输入进行相应的验证,允许同一页面中同时验证多个表单,熟悉接口之后也可以对特定的表单项甚至仅仅是某个字符串进行验证。因为是伪静态类,所以在调用时不需要实例化,直接以"类名+.语法+属性或方法名"来调用。此外,Validator还提供3种不同的错误提示模式,以满足不同的需要。 <br>Validator目前可实现的验证类型有: <br>1.是否为空;<br>2.中文字符;<br>3.双字节字符<br>4.英文;<br>5.数字;<br>6.整数;<br>7.实数;<br>8.Email地址;<br>9.使用HTTP协议的网址;<br>10.电话号码;<br>11.货币;<br>12.手机号码;<br>13.邮政编码;<br>14.身份证号码(1.05增强);<br>15.QQ号码;<br>16.日期;<br>17.符合安全规则的密码;<br>18.某项的重复值;<br>19.两数的关系比较;<br>20.判断输入值是否在(n, m)区间;<br>21.输入字符长度限制(可按字节比较);<br>22.对于具有相同名称的单选按钮的选中判断;<br>23.限制具有相同名称的多选按钮的选中数目;<br>24.自定义的正则表达式验证;<br>25.文件上传格式过滤(1.04新增)。 <br><br><br>不好意思哈,收一分钱(评价一下就是免费了)。<br>
js表单验证 表单的验证一直是网页设计者头痛的问题,表单验证类 Validator就是为解决这个问题而写的,旨在使设计者从纷繁复杂的表单验证中解放出来,把精力集中于网页的设计和功能上的改进上。 Validator是基于JavaScript技术的伪静态类和对象的自定义属性,可以对网页中的表单项输入进行相应的验证,允许同一页面中同时验证多个表单,熟悉接口之后也可以对特定的表单项甚至仅仅是某个字符串进行验证。因为是伪静态类,所以在调用时不需要实例化,直接以"类名+.语法+属性或方法名"来调用。此外,Validator还提供3种不同的错误提示模式,以满足不同的需要。 Validator目前可实现的验证类型有: 1.是否为空; 2.中文字符; 3.双字节字符 4.英文; 5.数字; 6.整数; 7.实数; 8.Email地址; 9.使用HTTP协议的网址; 10.电话号码; 11.货币; 12.手机号码; 13.邮政编码; 14.身份证号码(1.05增强); 15.QQ号码; 16.日期; 17.符合安全规则的密码; 18.某项的重复值; 19.两数的关系比较; 20.判断输入值是否在(n, m)区间; 21.输入字符长度限制(可按字节比较); 22.对于具有相同名称的单选按钮的选中判断; 23.限制具有相同名称的多选按钮的选中数目; 24.自定义的正则表达式验证; 25.文件上传格式过滤(1.04新增)。 更新历史: 1.01 修正对12月份的日期验证(感谢 flylg999 ) 1.03 修正Range验证类型时将数字当字符串比较的bug(感谢 cncom 和 xtlhnhbb ) 修正日期验证(感谢 Papsam ) 增加Username验证类型 增加对Phone验证类型时支持分机号 1.04 增加文件格式的过滤,用于上传时限制上传的文件格式 1.05 增强对身份证号码的验证 运行环境(客户端): 在Windows Server 2003下用IE6.0+SP1和Mozilla Firefox 1.0测试通过; 在Lunix RedHat 9下的Netscape测试通过;

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值