学习笔记 : Java web之文件的上传与下载

文件上传

如何实现文件上传

Apache组织提供了一个开源组件Commons-FileUpload( 依赖于common-io ),该组件可以方便地将multipart/form-data类型请求中的各种表单解析出来,并实现一个或多个文件的上传,同时也可以限制上传文件的大小等内容..并且性能优异,使用简单. FileUpload组件是通过Servlet来实现文件上传功能的,其工作流程如下图所示.

文件上传相关API

FileItem Interface

FileItem接口用于封装单个表单字段元素的数据,一个表单字段元素对应一个FileItem对象.

DiskFileItemFactory Class

DiskFileItemFactory类用于将请求的消息实体中的每一个文件封装成单独的FileItem对象.如果上传的文件比较小,将直接保存在内存中,如果上传的文件比较大,则会以临时文件的形式,保存在磁盘的临时文件中,默认情况下,文件保存在内存/磁盘临时文件夹的临界值为10240,既10KB.

ServletFileUpload Class

ServletFileUpload类是Apache组件处理文件上传的核心高级类,通过使用parseRequest(HttpServletRequest)方法可以将HTML中每个表单提交的数据封装成一个FileItem对象,然后以List列表的形式返回.

文件上传案例

  1. JSP页面代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    <%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>file</title>
    </head>
    <body>
    <h3 align="center">upload the specified the file</h3>
    <hr>
    <form action="UploadServlet2" method="post" enctype="multipart/form-data">
    <div align="center">
    <table>
    <tr>
    <td>学号</td>
    <td><input type="text" name="id"/>
    </tr>
    <tr>
    <td>姓名</td>
    <td><input type="text" name="name"/></td>
    </tr>
    <tr>
    <td>照片</td>
    <td><input type="file" name="image"/></td>
    </tr>
    <tr>
    <td>
    <%-- 文件上传结果 --%>
    <%
    String result = (String)request.getAttribute("result");
    if(result!=null){
    out.println("<script>alert(' "+result+" ')</script>");
    }
    %>
    </td>
    </tr>
    </table>
    <input type="submit" value="上传"/>
    </div>
    </form>
    </body>
    </html>
  2. Servlet类 : 获取表单及其上传文件的信息..

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    package pers.huangyuhui.file.servlet;

    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.util.List;

    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    import org.apache.commons.fileupload.FileItem;
    import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException;
    import org.apache.commons.fileupload.FileUploadException;
    import org.apache.commons.fileupload.disk.DiskFileItemFactory;
    import org.apache.commons.fileupload.servlet.ServletFileUpload;
    import org.apache.commons.io.filefilter.SuffixFileFilter;

    @WebServlet("/UploadServlet2")
    public class UploadServlet2 extends HttpServlet {
    private static final long serialVersionUID = 1L;

    public UploadServlet2() {
    super();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
    request.setCharacterEncoding("utf-8");
    response.setCharacterEncoding("utf-8");
    response.setContentType("text/html;charset=utf-8");

    String message = "上传失败 !";// 上传结果

    // FileItemFactory fileItemFactory = new DiskFileItemFactory();
    DiskFileItemFactory fileItemFactory = new DiskFileItemFactory();
    fileItemFactory.setSizeThreshold(1024 * 1024);// // 设置将上传的文件以临时文件的形式保存在磁盘的临界值
    // 默认情况下,采用的是系统默认的临时文件路径,可以通过该方式获取: System.getProperty("java.io.tmpdir");
    fileItemFactory.setRepository(new File("D:\\Java web\\temporary files"));// 将上传的文件以临时文件的形式保存在指定的目录下

    // 判断请求消息中的内容是否是: multipart/form-data类型
    if (ServletFileUpload.isMultipartContent(request)) {

    // 文件上传的核心高级类
    ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory);
    servletFileUpload.setSizeMax(1024 * 20); // 设置请求消息实体内容(既上传数据)的最大尺寸限制: 20KB
    try {
    // 将HTML中每个表单提交的数据封装成一个FileItem对象,然后以List列表的形式返回
    List<FileItem> fileItems = servletFileUpload.parseRequest(request);

    // 限制上传的文件类型
    String[] suffixs = new String[] { ".exe", ".bat", ".c", ".java", ".sh", "html", ".css", ".js" };
    SuffixFileFilter suffixFileFilter = new SuffixFileFilter(suffixs);

    // 遍历集合
    File file = null;
    String sno = null;
    String sname = null;
    String fileName = null;
    for (FileItem fileItem : fileItems) {
    String item = fileItem.getFieldName();
    if (fileItem.isFormField()) {// 普通文本表单字段
    if (item.equals("id")) {
    sno = fileItem.getString();
    } else if (item.contentEquals("name")) {
    sname = fileItem.getString();
    } else {
    System.out.println("others value ..");
    }
    } else {// 文件表单字段
    fileName = fileItem.getName();
    file = new File(request.getSession().getServletContext().getRealPath("/upload/" + fileName));
    if (suffixFileFilter.accept(file)) {
    message = "禁止上传此类型文件 !";
    } else {
    file.getParentFile().mkdirs();// 创建目录
    fileItem.write(file);// 上传文件
    message = "success to upload !";

    System.out.println("id: " + sno + "\nname: " + sname + "\nthe file name: " + fileName
    + "\nthe file path: " + file.getAbsolutePath()); // 上传者及文件信息
    }
    }
    }

    } catch (FileNotFoundException e) {
    message = "error : please choose the specified file !";
    } catch (SizeLimitExceededException e) {
    message = "error : the file size exceeds limit !";
    } catch (FileUploadException e) {
    e.printStackTrace();
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    // 将提示信息保留在request对象中
    request.setAttribute("result", message);
    request.getRequestDispatcher("upload.jsp").forward(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
    doGet(request, response);
    }

    }
  3. 程序运行结果 : 略..

文件下载

文件下载程序流程

浏览器通常会直接处理响应的实体内容,这时需要在HTTP响应消息中设置两个响应消息头字段,指定接收程序处理数据内容的方式为下载方式,当点击”下载”超链接时,系统将请求提交到对应的Servlet.在该Servlet中,程序流程如下 :

  1. 根据该地址创建文件字节输入流.
  2. 通过该流读取下载文件的内容.
  3. 将读取的内容通过输出流写到目标文件中.
  • HTTP中设置两个响应消息头,具体如下.
    1
    2
    3
    4
    //设定接收程序处理数据的方式
    Content-Disposition: attachment;filename=
    //设定实体内容的MIME类型
    Content-Type: application/x-msdownload

文件下载案例

  1. JSP页面代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    <!DOCTYPE html>
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html" charset="UTF-8">
    <title>down the file</title>
    </head>
    <body>
    <h3 align="center"></h3>
    <a href="DownloadServlet?filename=文件.png">
    Click to download this file ..
    </a>
    </body>
    </html>
  2. Servlet类 : 设置所要下载的文件以及文件在浏览器中打开的方式.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    package pers.huangyuhui.file.servlet;

    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.URLEncoder;

    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    import org.apache.tomcat.util.codec.binary.Base64;

    @WebServlet("/DownloadServlet")
    public class DownloadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    public DownloadServlet() {
    super();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
    response.setContentType("text/html;charset=utf-8");

    // 获取需要下载的文件名
    String fileName = request.getParameter("filename");
    // 设定接收程序处理数据的方式( MIME type : application/octet-stream )
    response.addHeader("Content-Type", "application/octet-stream");

    /*
    * 根据不同浏览器进行不同的编码处理,以防乱码
    */
    // User-Agent: 首部包含了一个特征字符串,用来让网络协议的对端来识别发起请求的用户代理软件的应用类型、操作系统、软件开发商以及版本号.
    String agent = request.getHeader("User-Agent");
    if (agent.toLowerCase().indexOf("firefox") != -1) {
    response.addHeader("Content-Disposition", "attachment;filename==?UTF-8?B?"
    + new String(Base64.encodeBase64(fileName.getBytes("UTF-8"))) + "?=");
    } else {
    // 设定实体内容的MIME类型并使用encode(String s, Charset charset)解决乱码问题: Google Chrome,Microsoft Eage ..
    response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
    }

    // 读取文件
    InputStream inputStream = getServletContext().getResourceAsStream("/resource/" + fileName);
    // 获取response对象的输出流
    OutputStream outputStream = response.getOutputStream();
    byte[] buffer = new byte[1024];
    int len;
    while ((len = inputStream.read(buffer)) != -1) {
    outputStream.write(buffer, 0, len);
    }
    outputStream.close();
    inputStream.close();
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
    doGet(request, response);
    }

    }
  3. 程序运行结果 : 略..