• javaweb 之 文件上传与下载


    1、文件上传的原理分析

    1.1文件上传的必要前提:

    • a、提供form表单,method必须是post
    • b、form表单的enctype必须是multipart/form-data
    • c、提供input type="file"类的上传输入域

    1.2enctype属性

    作用:告知服务器请求正文的MIME类型。(请求消息头:Content-Type作用是一致的)

    可选值:application/x-www-form-urlencoded(默认):

    正文:name=admin&password=123

    服务器获取数据:String name = request.getParameter("name");

    multipart/form-data:

    正文

    服务器获取数据:request.getParameter(String)方法获取指定的表单字段字符内容,但文件上传表单已经不在是字符内容,而是字节内容,所以失效。

    文件上传:解析请求正文的每部分的内容。

    2、借助第三方的上传组件实现文件上传

    2.1 fileupload概述

    fileupload是由apache的commons组件提供的上传组件。它最主要的工作就是帮我们解析request.getInputStream()。

    导入commons-fileupload相关jar包

    1. commons-fileupload.jar,核心包;
    2. commons-io.jar,依赖包。

    2.2 fileupload的核心类有:

    1. DiskFileItemFactory  和磁盘打交道(工厂模式)
    2. ServletFileUpload   核心对象  操作fileitem  
    3. FileItem         表单对象

    a、解析原理

    2.3 fileupload简单应用

    使用fileupload组件的步骤如下:

    创建工厂类DiskFileItemFactory对象:  DiskFileItemFactory factory = new DiskFileItemFactory()

    使用工厂创建解析器对象:         ServletFileUpload fileUpload = new ServletFileUpload(factory)

    使用解析器来解析request对象:      List<FileItem> list = fileUpload.parseRequest(request)

    FileItem对象对应一个表单项(表单字段)。可以是文件字段或普通字段

    •  boolean isFormField():判断当前表单字段是否为普通文本字段,如果返回false,说明是文件字段;
    •  String getFieldName():获取字段名称,例如:<input type=”text” name=”username”/>,返回的是username;
    •  String getString():获取字段的内容,如果是文件字段,那么获取的是文件内容,当然上传的文件必须是文本文件;
    •  String getName():获取文件字段的文件名称;(a.txt)
    •  String getContentType():获取上传的文件的MIME类型,例如:text/plain。
    •  int getSize():获取上传文件的大小;
    •  InputStream getInputStream():获取上传文件对应的输入流;
    •  void write(File):把上传的文件保存到指定文件中。
    • delete();

    3、文件上传时要考虑的几个问题(经验分享)

    a、保证服务器的安全

      把保存上传文件的目录放在用户直接访问不到的地方。

    b、避免文件被覆盖

      让文件名唯一即可

    c、避免同一个文件夹中的文件过多

    • 方案一:按照日期进行打散存储目录
    • 方案二:用文件名的hashCode计算打散的存储目录:二级目录

    d、限制文件的大小:web方式不适合上传大的文件

    • 单个文件大小:ServletFileUpload.setFileSizeMax(字节)
    • 总文件大小:(多文件上传)ServletFileUpload.setSizeMax(字节)

    e、上传字段用户没有上传的问题

      通过判断文件名是否为空即可

    f、临时文件的问题

    DiskFileItemFactory:  作用:产生FileItem对象

    内部有一个缓存,缓存大小默认是10Kb。如果上传的文件超过10Kb,用磁盘作为缓存。

    存放缓存文件的目录在哪里?默认是系统的临时目录。

    如果自己用IO流实现的文件上传,要在流关闭后,清理临时文件。FileItem.delete();

      1 package com.upload;
      2 
      3 import java.io.File;
      4 import java.io.FileOutputStream;
      5 import java.io.IOException;
      6 import java.io.InputStream;
      7 import java.text.SimpleDateFormat;
      8 import java.util.Date;
      9 import java.util.List;
     10 import java.util.UUID;
     11 import java.util.logging.SimpleFormatter;
     12 
     13 import javax.servlet.ServletException;
     14 import javax.servlet.http.HttpServlet;
     15 import javax.servlet.http.HttpServletRequest;
     16 import javax.servlet.http.HttpServletResponse;
     17 
     18 import org.apache.commons.fileupload.FileItem;
     19 import org.apache.commons.fileupload.FileUploadBase;
     20 import org.apache.commons.fileupload.FileUploadException;
     21 import org.apache.commons.fileupload.disk.DiskFileItemFactory;
     22 import org.apache.commons.fileupload.servlet.ServletFileUpload;
     23 import org.apache.commons.io.FilenameUtils;
     24 
     25 import com.util.UUIDUtil;
     26 
     27 public class UploadServlet2 extends HttpServlet {
     28 
     29     public void doGet(HttpServletRequest request, HttpServletResponse response)
     30             throws ServletException, IOException {
     31         //执行文件上传的操作
     32         //判断是否写了那个multipart/form-data 即是否支持文件上传
     33         boolean isMultipartContent = ServletFileUpload.isMultipartContent(request);
     34         if(!isMultipartContent){
     35             throw new RuntimeException("your form is not multipart/form-data ");
     36         }
     37         //创建一个DiskFileItemfactory工厂类
     38         DiskFileItemFactory factory = new DiskFileItemFactory();
     39         //保存临时文件的目录
     40         factory.setRepository(new File("f:\"));
     41         //创建一个servletFileUpload核心对象(表单解析器)
     42         ServletFileUpload sfu = new ServletFileUpload(factory);
     43         
     44         //解析request对象,并得到一个表单项的集合
     45         try {
     46             //sfu.setFileSizeMax(1024*1024*3);//3M
     47             
     48             sfu.setFileSizeMax(1024*1024*36);
     49             
     50             List<FileItem> fileItems = sfu.parseRequest(request);
     51             //遍历表单项
     52             for (FileItem fileItem : fileItems) {
     53                 if(fileItem.isFormField()){
     54                     //普通表单项
     55                     processFormField(fileItem);
     56                 }else{
     57                     //上传表单项
     58                     processUploadField(fileItem);
     59                 }
     60             }
     61             
     62         }catch (FileUploadBase.FileSizeLimitExceededException e) {
     63             //throw new RuntimeException("文件过大,不能超过3M");
     64             System.out.println("文件过大,不能超过3M");
     65         }
     66         catch (FileUploadException e) {
     67             e.printStackTrace();
     68         }
     69     }
     70     //上传表单项
     71     private void processUploadField(FileItem fileItem) {
     72         
     73         try {
     74             //得到文件输入流
     75             InputStream is = fileItem.getInputStream();
     76             //创建一个文件存盘的目录
     77             String directoryRealPath = this.getServletContext().getRealPath("/WEB-INF/upload");
     78             //既代表文件有代表目录
     79             File storeDirectory = new File(directoryRealPath);
     80             if(!storeDirectory.exists()){
     81                 storeDirectory.mkdirs();
     82             }
     83             //得到文件名
     84             String filename = fileItem.getName();
     85             //filename = filename.substring(filename.lastIndexOf(File.separator)+1);
     86             if(filename!=null){    
     87                 filename = FilenameUtils.getName(filename);
     88                 if(filename=="")
     89                     System.out.println("空文件");
     90             }
     91             //解决重名问题 UUID亦可以
     92             filename = UUIDUtil.getUUID()+"_"+filename;
     93             //目录打散
     94             //String childDirectory = makeChildDirectory(storeDirectory);
     95             
     96             String childDirectory = makeChildDirectory(storeDirectory,filename);
     97             
     98             //在storeDirectory目录下构建一个完整目录下的文件 
     99             File file = new File(storeDirectory,childDirectory+File.separator+filename);
    100             //通过文件输出流将上传的文件保存到磁盘
    101             FileOutputStream fos = new FileOutputStream(file);
    102             int len = 0;
    103             byte[] b = new byte[1024];
    104             while((len = is.read(b))!= -1){
    105                 fos.write(b,0,len);
    106             }
    107             fos.close();
    108             is.close();
    109             fileItem.delete();
    110         } catch (IOException e) {
    111             e.printStackTrace();
    112         }
    113         
    114     }
    115     //按目录打散
    116     private String makeChildDirectory(File storeDirectory, String filename) {
    117         int hascode = filename.hashCode();//返回字符串转换的32位hascode码 int值
    118         
    119         String code = Integer.toHexString(hascode);//把hascode转换成十六进制的字符     assdas3234af
    120         
    121         String childDirectory = code.charAt(0)+File.separator+code.charAt(1);
    122         File file = new File(storeDirectory,childDirectory);
    123         if(!file.exists()){
    124             file.mkdirs(); 
    125         }
    126         return childDirectory;
    127     }
    128     /*//按日期打散
    129     private String makeChildDirectory(File storeDirectory) {
    130         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    131         String dateDirectory = sdf.format(new Date());
    132         //只管创建目录
    133         File file = new File(storeDirectory,dateDirectory);
    134         if(!file.exists()){
    135             file.mkdirs(); 
    136         }
    137         return dateDirectory;
    138     }*/
    139     //普通表单项
    140     private void processFormField(FileItem fileItem) {
    141         String fieldname = fileItem.getFieldName();
    142         String fieldvalue = fileItem.getString();
    143         System.out.println(fieldname+"="+fieldvalue);
    144         
    145     }
    146 
    147     public void doPost(HttpServletRequest request, HttpServletResponse response)
    148             throws ServletException, IOException {
    149         doGet(request, response);
    150     }
    151 
    152 }

    中文编码问题

     1 private void processUploadField(FileItem fileItem) {
     2         // 得到文件输入流
     3         try {
     4             InputStream is = fileItem.getInputStream();
     5             // 创建一个文件存盘的目录
     6             String directoryRealPath = this.getServletContext().getRealPath(
     7                     "/WEB-INF/upload");
     8             // 既代表文件有代表目录
     9             File storeDirectory = new File(directoryRealPath);
    10             if (!storeDirectory.exists()) {
    11                 storeDirectory.mkdirs();
    12             }
    13             // 得到文件名
    14             String filename = fileItem.getName();
    15             if (filename != null) {
    16                 filename = FilenameUtils.getName(filename);
    17                 if (filename == "")
    18                     System.out.println("空文件");
    19             }
    20             // 解决重名问题 UUID亦可以
    21             filename = UUIDUtil.getUUID() + "_" + filename;
    22             String childDirectory = makeChildDirectory(storeDirectory, filename);
    23             // 上传文件自动删除临时文件 
    24             /*
    25              * fileItem.write(new
    26              * File(storeDirectory,childDirectory+File.separator+filename));
    27              * fileItem.delete();
    28              */
    29             // 上传文件,自动删除临时文件
    30             fileItem.write(new File(storeDirectory, childDirectory
    31                     + File.separator + filename));
    32             fileItem.delete();
    33         } catch (IOException e) {
    34             e.printStackTrace();
    35         } catch (Exception e) {
    36             e.printStackTrace();
    37         }
    38     }

     注:这里32行的fileItem.delete();是多余的,请看fileItem.write的帮助文档:(意思大概是说只想去上传,不会保留临时文件)

    而有个问题是在所设置的目录下却还会出现临时文件这是为什么呢??

    原来是在List<FileItem> fileItems = sfu.parseRequest(request);就创建了 在write里可以自动删除掉temp。

    下面放一张大佬给的示例(佐证 在有inputstream时 file无法delete和renameTo操作)

    4、文件的下载

     1 package com.servlet;
     2 
     3 import java.io.IOException;
     4 import java.io.PrintWriter;
     5 import java.net.URLEncoder;
     6 
     7 import javax.servlet.ServletException;
     8 import javax.servlet.http.HttpServlet;
     9 import javax.servlet.http.HttpServletRequest;
    10 import javax.servlet.http.HttpServletResponse;
    11 
    12 public class DownloadServlet extends HttpServlet {
    13 
    14     public void doGet(HttpServletRequest request, HttpServletResponse response)
    15             throws ServletException, IOException {
    16         //设置一个要下载的文件
    17         String filename = "销售榜单.csv";
    18         
    19         //设置文件名的编码
    20         if(request.getHeader("user-agent").toLowerCase().contains("msie")){
    21             filename = URLEncoder.encode(filename, "UTF-8");//将不安全的文件名改为UTF-8格式
    22         }else{
    23             filename = new String(filename.getBytes("UTF-8"),"iso-8859-1");//火狐浏览器
    24         }
    25         //告知浏览器要下载文件
    26         response.setHeader("content-disposition", "attachment;filename="+filename);
    27         //response.setHeader("content-type", "image/jpeg");
    28         //根据文件名自动获得文件类型
    29         response.setContentType(this.getServletContext().getMimeType(filename));
    30         //告知服务器使用什么编码
    31         response.setCharacterEncoding("UTF-8");
    32         //创建一个文件输出流
    33         PrintWriter out = response.getWriter();
    34         out.write("电视机,20
    ");
    35         out.write("洗衣机,10
    ");
    36         out.write("冰箱,8
    ");
    37     }
    38 
    39     public void doPost(HttpServletRequest request, HttpServletResponse response)
    40             throws ServletException, IOException {
    41         doGet(request, response);
    42     }
    43 
    44 }
  • 相关阅读:
    java中将表单转换为PDF
    base64图片
    ORACLE中用户等系统信息操作
    jquery中live is not a function的问题
    完全卸载Oracle11G
    jquery 获取鼠标和元素的坐标点
    JS的多线程
    Oracle和SQLServer解锁杀进程
    JAVA 通过LDAP获取AD域用户及组织信息
    oracle基础语法大全
  • 原文地址:https://www.cnblogs.com/lzhh/p/javaweb_1.html
Copyright © 2020-2023  润新知