使用servlet3.1注解实现文件上传详细例子

简介

之前的使用servlet3.0实现文件上传的文章中有一些不太完善的地方,所以写这篇文章完善一下。

上传是文件名重复的问题

下面代码是之前servlet3.0实现文件上传的文章中的上传方式:

package com.monkey1024.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;

/**
 * 处理上传的servlet
 */
@WebServlet("/upload")
@MultipartConfig //表示当前servlet可以处理multipart请求
public class UploadServlet01 extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取服务器存放上传文件的路径
        String path = this.getServletContext().getRealPath("/upload");
        System.out.println(path);
        //获取上传文件,photo是html表单中的name
        Part part = request.getPart("photo");
        //获取上传文件的名称,这是servlet3.1中加入的方法
        String fileName = part.getSubmittedFileName();
        System.out.println(path + "/" + fileName);
        //将上传的文件写入到指定的服务器路径中
        part.write(path + "/" + fileName);

    }

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

}

上面代码中有可能会出现多个用户上传的文件名重复的情况,此时之前在服务器中存储的文件会被后上传的同名文件替换掉,为了解决这个问题,可以将文件名修改一下,建议使用UUID的方式重命名。
uuid是Universally Unique Identifier的缩写,中文是通用统一识别码,uuid具有唯一性,uuid的生成跟系统的时间、mac地址、时间序列、随机数有关,所以通常所生成的uuid是不会重复的,两个相同的uuid出现的概率非常低(比陨石撞击地球的概率还要低)。

    //获取上传文件的名称,这是servlet3.1中加入的方法
    String fileName = part.getSubmittedFileName();

    //在文件名中添加uuid
    fileName = UUID.randomUUID() + "_" + fileName;

通过上面的代码就可以确保用户上传文件名的唯一性了。

创建目录便于管理

如果用户上传的文件都放到一个文件夹下的话,随着时间的积累,该文件夹就会变的非常臃肿,不利于管理。因此,这里考虑将用户上传的文件放到不同的文件夹中,我们来按照年、月、日创建多级子目录的方式。比如用户在2018年1月25日上传了一个文件monkey.jpg,那就让该文件放到这样的文件夹目录中:/2018/1/25/monkey.jpg

//获取当前系统时间的年月日
LocalDate now = LocalDate.now();
int year = now.getYear();
int month = now.getMonthValue();
int day = now.getDayOfMonth();
String path = this.getServletContext().getRealPath("/upload");
//在upload下分别创建年、月、日三级子目录
path = path + "/" + year + "/" + month + "/" +day;
//创建父目录
File parentDir = new File(path);
//如果父目录不存在,则创建
if(!parentDir.exists()){
    parentDir.mkdirs();
}

修改后的代码:

package com.monkey1024.servlet;

import java.io.File;
import java.io.IOException;
import java.time.LocalDate;
import java.util.UUID;

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

/**
 * 处理上传的servlet
 */
@WebServlet("/upload")
@MultipartConfig //表示当前servlet可以处理multipart请求
public class UploadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取服务器存放上传文件的路径
        String path = this.getServletContext().getRealPath("/upload");
        System.out.println(path);
        //获取上传文件,photo是html表单中的name
        Part part = request.getPart("photo");
        //获取上传文件的名称,这是servlet3.1中加入的方法
        String fileName = part.getSubmittedFileName();

        //在文件名中添加uuid
        fileName = UUID.randomUUID() + "_" + fileName;

      //获取当前系统时间的年月日
        LocalDate now = LocalDate.now();
        int year = now.getYear();
        int month = now.getMonthValue();
        int day = now.getDayOfMonth();

        //在upload下分别创建年、月、日三级子目录
        path = path + File.separator + year + File.separator + month + File.separator +day;
        //创建父目录
        File parentDir = new File(path);
        //如果父目录不存在,则创建
        if(!parentDir.exists()){
            parentDir.mkdirs();
        }

        //将上传的文件写入到指定的服务器路径中
        part.write(path + File.separator + fileName);
    }

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


}

设置上传文件的大小

在@MultipartConfig注解中有两个属性:
maxFileSize:表示上传一个文件的最大值,单位是byte
maxRequestSize:表示一次请求中上传文件的最大值,一次可能上传多个文件,这些文件大小的之和。单位是byte

如果上传的文件超出了设置的最大值时,系统会在

Part part = request.getPart("photo");

抛出一个IllegalStateException的异常,我们可以通过捕获该异常从而向用户提示友好信息。

package com.monkey1024.servlet;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.time.LocalDate;
import java.util.UUID;

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

/**
 * 处理上传的servlet
 */
@WebServlet("/upload")
@MultipartConfig(maxFileSize = 1024*5) // 表示当前servlet可以处理multipart请求
public class UploadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    private static MultipartConfig config = UploadServlet.class.getAnnotation(MultipartConfig.class);

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        PrintWriter out = response.getWriter();
        // 获取服务器存放上传文件的路径
        String path = this.getServletContext().getRealPath("/upload");
        System.out.println(path);
        // 获取上传文件,photo是html表单中的name
        Part part;
        try{
            part = request.getPart("photo");
        }catch(IllegalStateException e){
            //上传的单个文件超出maxFileSize或者上传的总的数据量超出maxRequestSize时会抛出此异常
            e.printStackTrace();
            out.write("文件上传失败,请上传小于5kb的文件");
            return;
        }
        // 获取上传文件的名称,这是servlet3.1中加入的方法
        String fileName = part.getSubmittedFileName();

        // 在文件名中添加uuid
        fileName = UUID.randomUUID() + "_" + fileName;

        // 获取当前系统时间的年月日
        LocalDate now = LocalDate.now();
        int year = now.getYear();
        int month = now.getMonthValue();
        int day = now.getDayOfMonth();

        // 在upload下分别创建年、月、日三级子目录
        // path = path + "/" + year + "/" + month + "/" +day;
        path = path + File.separator + year + File.separator + month + File.separator + day;
        // 创建父目录
        File parentDir = new File(path);
        // 如果父目录不存在,则创建
        if (!parentDir.exists()) {
            parentDir.mkdirs();
        }
        System.out.println(path);
        // 将上传的文件写入到指定的服务器路径中
        part.write(path + File.separator + fileName);
        out.write("文件上传成功!");
    }

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

}

多文件上传

用户在客户端有时需要进行多文件上传,此时可以通过下面方法获取Part对象的集合:

Collection<Part> parts = request.getParts();

然后遍历该集合分别处理part对象即可:

package com.monkey1024.servlet;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.time.LocalDate;
import java.util.Collection;
import java.util.UUID;

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

/**
 * 处理上传的servlet
 */
@WebServlet("/upload")
@MultipartConfig(maxRequestSize = 1024*5) // 表示当前servlet可以处理multipart请求
public class UploadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    private static MultipartConfig config = UploadServlet.class.getAnnotation(MultipartConfig.class);

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        PrintWriter out = response.getWriter();
        // 获取服务器存放上传文件的路径
        String path = this.getServletContext().getRealPath("/upload");
        System.out.println(path);
        // 一次上传多个文件
        Collection<Part> parts;
        try {
            parts = request.getParts();
        } catch (IllegalStateException e) {
            // 上传的单个文件超出maxFileSize或者上传的总的数据量超出maxRequestSize时会抛出此异常
            e.printStackTrace();
            out.write("文件上传失败,请上传小于5kb的文件");
            return;
        }

        // 获取当前系统时间的年月日
        LocalDate now = LocalDate.now();
        int year = now.getYear();
        int month = now.getMonthValue();
        int day = now.getDayOfMonth();

        // 在upload下分别创建年、月、日三级子目录
        // path = path + "/" + year + "/" + month + "/" +day;
        path = path + File.separator + year + File.separator + month + File.separator + day;
        // 创建父目录
        File parentDir = new File(path);
        // 如果父目录不存在,则创建
        if (!parentDir.exists()) {
            parentDir.mkdirs();
        }

        for (Part part : parts) {
            // 获取上传文件的名称,这是servlet3.1中加入的方法
            String fileName = part.getSubmittedFileName();
            if("".equals(fileName)) {
                //如果文件名为"",说明这个文件是空,无需进行上传处理
                continue;
            }
            // 在文件名中添加uuid
            fileName = UUID.randomUUID() + "_" + fileName;
            System.out.println(path);
            // 将上传的文件写入到指定的服务器路径中
            part.write(path + File.separator + fileName);
        }

        out.write("文件上传成功!");
    }

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

}

bootstrap-fileupload组件

如果希望实现一些更炫的前端效果,可以借助使用bootstrap-fileupload,该组件基于bootstrap实现,使用前需要引入bootstrap相关文件,里面的文档和demo都非常丰富,感兴趣的同学可以自行查看。
在线文档:http://plugins.krajee.com/file-input
在线demo:http://plugins.krajee.com/file-input/demo