分享

基于表单的文件上传

 jl_oracle 2014-05-06
基于表单的文件上传 2010-04-05 21:13:27

分类:

  1. 如果在表单中使用表单元素 <input type=“file” />,浏览器在解析表单时,会自动生成一个输入框和一个按钮,输入框可供用户填写本地文件的文件名和路径名,按钮可以让浏览器打开一个文件选择框供用户选择文件:   
  2.   
  3.     
  4.   
  5. 当表单需要上传文件时,需指定表单 enctype 的值为 multipart/form-data   
  6.   
  7. 在 form 元素的语法中,enctype 属性指定将数据发送到服务器时浏览器使用的编码类型。    
  8.   
  9. enctype 属性取值:   
  10.   
  11. application/x-www-form-urlencoded:表单 enctype 属性的默认值。这种编码方案使用有限的字符集,当使用了非字母和数字时,必须用”%HH”代替(H 代表十六进制数字)。对于大容量的二进制数据或包含非 ASCII 字符的文本来说,这种编码不能满足要求。   
  12.   
  13. multipart/form-data:form 设定了enctype=“multipart/form-data”属性后,表示表单以二进制传输数据 .   
  14.   
  15.     
  16.   
  17. Commons-fileupload 组件上传的基本原理   
  18.   
  19. FileUpload组件将页面提交的所有元素(普通form表单域,如text和文件域file)都看作一样的FileItem,这样上传页面提交的 request请求也就是一个FileItem的有序组合,FileUpload组件可以解析该request,并返回一个一个的FileItem。而对每一个FileItem,FileUpload组件可以判断出它是普通form表单域还是文件file域,从而根据不同的类型,采取不同的操作--如果是表单域,就读出其值,如果是文件域,就保存文件到服务器硬盘上或者内存中   
  20.   
  21.     
  22.   
  23. Commons-fileupload 组件API   
  24.   
  25. ServletFileUpload 负责处理上传的文件数据,并将每部分的数据封装成一到 FileItem 对象中。   
  26.   
  27. DiskFileItemFactory 是创建 FileItem 对象的工厂,在这个工厂类中可以配置内存缓冲区大小和存放临时文件的目录。   
  28.   
  29. ServletFileUpload 在接收上传文件数据时,会将内容保存到内存缓存区中,如果文件内容超过了 DiskFileItemFactory 指定的缓冲区的大小,那么文件将被保存到磁盘上,存储为 DiskFileItemFactory 指定目录中的临时文件。等文件数据都接收完毕后,ServletUpload 在从文件中将数据写入到上传文件目录下的文件中   
  30.   
  31.     
  32.   
  33.     
  34.   
  35. 进行文件上传和下载的具体操作:   
  36.   
  37. 首先,加载必要的jar包:“commons-fileupload-1.2.1.jar”,“commons-io-1.4.jar”   
  38.   
  39. 步骤:   
  40.   
  41. 0. 创建 FileItemFactory 子类 DiskFileItemFactory 的对象   
  42.   
  43. 1.为了得到 ServletFileUpload 对象, 先需要得到 FileItemFactory 的一个对象, 然后调用 new ServletFileUpload(FileItemFactory fif); 方法得到 ServletFileUpload 对象   
  44.   
  45. 2.为了得到 FileItem 的集合, 先需要得到 ServletFileUpload 对象, 然后调用该对象的 parseRequest() 方法得到 FileItem 的 List   
  46.   
  47. 3.从 HttpServletRequest 对象中得到 FileItem 的集合   
  48.   
  49. 4. 对上述集合进行遍历:判断是表单域还是文件域   
  50.   
  51.     
  52.   
  53. 下面是对文件上传相关操作的代码示例。   
  54.   
  55. //1. 验证是否使用 FileUpload 组件   
  56.   
  57.         boolean isMultipart = ServletFileUpload.isMultipartContent(request);   
  58.   
  59.           
  60.   
  61.         //2.1  不使用   
  62.   
  63.         if(!isMultipart){   
  64.   
  65.             response.getWriter().println("没有文件域");   
  66.   
  67.             return;   
  68.   
  69.         }   
  70.   
  71.           
  72.   
  73.         //2.2 使用   
  74.   
  75.           
  76.   
  77.         //3. 获取 DiskFileItemFactory 对象   
  78.   
  79.         DiskFileItemFactory itemFactory = new DiskFileItemFactory();   
  80.   
  81.           
  82.   
  83.         //4. 获取 ServletFileUpload 对象   
  84.   
  85.         ServletFileUpload fileUpload = new ServletFileUpload();   
  86.   
  87.         fileUpload.setFileItemFactory(itemFactory);   
  88.   
  89.         设置上传文件的最大大小   
  90.   
  91.         fileUpload.setFileSizeMax(1000 * 100);   
  92.   
  93.           
  94.   
  95.         String forwardPage = null;   
  96.   
  97.           
  98.   
  99.         try {   
  100.   
  101.             //5. 调用 ServletFileUpload 的 parseRequest() 方法, 获取 FileItem 的集合   
  102.   
  103.             List<FileItem> items = fileUpload.parseRequest(request);   
  104.   
  105.               
  106.   
  107.             Map<String, String> paramMap = new HashMap<String, String>();   
  108.   
  109.             FileItem fileItem = null;   
  110.   
  111.               
  112.   
  113.             //6. 对 5 获取的集合进行遍历   
  114.   
  115.             Iterator<FileItem> it = items.iterator();   
  116.   
  117.             while(it.hasNext()){   
  118.   
  119.                 FileItem item = it.next();   
  120.   
  121.                 //7. 验证是否为表单域   
  122.   
  123.                 if(item.isFormField()){   
  124.   
  125.                     //7.1 是表单域, 把获取到得 表单域的 fieldName 和 value 放在 map 中.   
  126.   
  127.   获得表单中该字段的字段名   
  128.   
  129.                     String fieldName = item.getFieldName();   
  130.   
  131. 获得表单中与上面字段名对应的字段值   
  132.   
  133.                     String value = item.getString("UTF-8");   
  134.   
  135.                       
  136.   
  137.                     paramMap.put(fieldName, value);   
  138.   
  139.                 }   
  140.   
  141.                     //7.2 不是表单域, 即为文件域. 获取 FileItem 对象, 注意限制条件.   
  142.   
  143.                 else{   
  144.   
  145. 这里获得了与file对应的FileItem对象   
  146.   
  147.                     fileItem = item;   
  148.   
  149.                 }   
  150.   
  151.                       
  152.   
  153.             }   
  154.   
  155.               
  156.   
  157.             //8. 对 7.2 获取的 FileItem 对象进行文件上传操作.   
  158.   
  159.             //10.1 获取全路径名   
  160.   
  161.             String path = null;   
  162.   
  163.               
  164.   
  165.             path = this.getServletContext().getRealPath("/photoes");   
  166.   
  167.               
  168.   
  169.             File photoesDir = new File(path);   
  170.   
  171.             if(!photoesDir.exists()){   
  172.   
  173.                 photoesDir.mkdir();   
  174.   
  175.             }   
  176.   
  177.               
  178.   
  179.             path = path + "/" + paramMap.get("username");   
  180.   
  181.               
  182.   
  183.             File file = new File(path + ".jpg");   
  184.   
  185.               
  186.   
  187.             //10.2 上传操作,把用户在页面上传的文件保存到服务器   
  188.   
  189.             fileItem.write(file);   
  190.   
  191.     
  192.   
  193. 上传文件表单的两个准备工作   
  194.   
  195. 1. 提交方式改为 POST   
  196.   
  197. 2. enctype 属性的取值改为 multipart/form-data   
  198.   
  199.     
  200.   
  201. 在获取表单值时   
  202.   
  203. 1. 不能使用 request.getParameter() 方法, 因为该方法仅能获取字符串, 对二进制无能为力.   
  204.   
  205. 2. 使用 fileUpload 组件读取上传文件及表单域字段.   
  206.   
  207.     
  208.   
  209. fileUpload 组件的核心思想   
  210.   
  211. fileUpload 组件认为: 所有的表单域(包含文件域和文本域)都是 FileItem 对象.   
  212.   
  213. 步骤:   
  214.   
  215. 1. 获取 FileItem 对象组成的 List   
  216.   
  217. 2. 对 List 进行遍历   
  218.   
  219. 3. 判断每一个  FileItem 对象是表单域 还是 文件域, 以进行不同的处理.   
  220.   
  221.     
  222.   
  223. 详细步骤:   
  224.   
  225. 1. 检查表单的 enctype 是否为 multipart/form-data, 以决定是否使用 FileUpload 组件进行解析请求   
  226.   
  227. 2. 若表单的 enctype 是否为 multipart/form-data, 则需要获取 封装了所有表单域 的对应的 FileItem 对象的 List: List<FileItem> list = null;   
  228.   
  229.    3) 获取 ServletFileUpload 对象, 调用该对象的 public java.util.List parseRequest(javax.servlet.http.HttpServletRequest request)   
  230.   
  231.                方法获取封装 FileItem 的List   
  232.   
  233.    2) 需要使用 ServletFileUpload 的构造器来创建 ServletFileUpload 对象:   
  234.   
  235.       ^ 使用任意一个构造器都可以得到 ServletFileUpload 对象, 但都需要先得到 FileItemFactory 对象   
  236.   
  237.       * ServletFileUpload upload = new ServletFileUpload();   
  238.   
  239.           upload.setFileItemFactory(factory);   
  240.   
  241.         * ServletFileUpload upload = new ServletFileUpload(factory);   
  242.   
  243.    1) 创建 FileItemFactory 接口是想类对象: DiskFileItemFactory, 直接创建该对象.   
  244.   
  245. 3. 可以调用 DiskFileItemFactory 的相关方法来对文件上传进行一些优化:   
  246.   
  247.    1) setSizeThreshold(int sizeThreshold):设置使用临时文件夹的大小.   
  248.   
  249.    2) setRepository(): 设置临时文件夹   
  250.   
  251. 4. 可以调用 ServletUpload 的方法进行文件上传的一些控制   
  252.   
  253.    1) setFileSizeMax: 设置单个文件的最大上传值   
  254.   
  255.    2) setSizeMax:设置总上传文件的最大值, 以 byte 为单位.   
  256.   
  257. 5. 关于中文乱码的问题:   
  258.   
  259.    1) 在解析 request 之前: request.setCharacterEncoding("UTF-8"); 可以解决文件域的乱码问题   
  260.   
  261.    2) 在解析表单域时需要使用: new String(value.getBytes("iso-8859-1"), "UTF-8"); 解决中文乱码问题.   
  262.   
  263. 但是有时候,即使我们按照上面方式设置了,可以还是中文乱码,这个时候问题多半就出在tomcat的配置上了,打开tomcat的service.xml文件,在那里面注意下面部分   
  264.   
  265.  <Connector port="8989" protocol="HTTP/1.1"  
  266.   
  267.                connectionTimeout="20000"  
  268.   
  269.                redirectPort="8443" useBodyEncodingForURI="true"/>   
  270.   
  271. 只有加上了红色部分的设置,我们在程序中配置EncodingUrl才能起作用。   
  272.   
  273.     
  274.   
  275. 下面一段代码是文件下载的相关操作   
  276.   
  277. //1. 设置响应报头 contentType: application/x-msdownload -->   
  278.   
  279.         //   告诉浏览器其所输出的内容的类型不是普通的文本文件或 HTML 文件,而是一个要保存到本地的下载文件   
  280.   
  281.         //response.setHeader("conent-type", "application/x-msdownload");   
  282.   
  283.         response.setContentType("application/x-msdownload");   
  284.   
  285.           
  286.   
  287.         //2. 设置响应抱头 Content-Disposition: attachment -->   
  288.   
  289.         //   Web 服务器希望浏览器不直接处理相应的实体内容,而是由用户选择将相应的实体内容保存到一个文件中   
  290.   
  291.         response.setHeader("Content-Disposition""attachment; filename=haha.ppt");   
  292.   
  293.           
  294.   
  295.         //3. 利用 response.getOutputStream() 进行 io 操作   
  296.   
  297.         OutputStream os = response.getOutputStream();   
  298.   
  299.         BufferedOutputStream bos = new BufferedOutputStream(os);   
  300.   
  301.           
  302.   
  303.         //文件的输入流   
  304.   
  305.         InputStream is = null;   
  306.   
  307.         is = new FileInputStream("E:\\source\\090515\\javaee\\fileupload\\fileUpload.ppt");   
  308.   
  309.         BufferedInputStream bis = new BufferedInputStream(is);   
  310.   
  311.           
  312.   
  313.         int length = 0;   
  314.   
  315.         byte [] temp = new byte[1 * 1024 * 10];   
  316.   
  317.         while((length = bis.read(temp)) != -1){   
  318.   
  319.             bos.write(temp, 0, length);   
  320.   
  321.             System.out.println("循环操作中...");   
  322.   
  323.         }   
  324.   
  325.           
  326.   
  327.         bis.close();   
  328.   
  329.         bos.close();   
  330.   
  331.     
  332.   
  333. 但是上面的这种方式无法被迅雷之类的下载工具下载,如果希望被迅雷之类的东西下载,就直接这么写就可以了   
  334.   
  335. request.getRequestDispatcher("/data/fileUpload.ppt")   
  336.   
  337.             .forward(request, response);  

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多