Java Web 文件上传

文件上传

一、注意事项

  1. 为保证服务器安全,上传文件应当保存在外界无法直接访问的路径。(如WEB-INF目录下)
  2. 为防止文件覆盖,要为上传的文件生成一个唯一的文件名。(如-时间戳,-uuid,-md5,-位运算算法)
  3. 要限制上传文件的大小的最大值。
  4. 可以限制上传文件的类型,在获取上传文件名时,判断后缀名是否合法。

二、组件

浏览器处理上传文件,是将文件以流的形式提交到服务器端。

  • commons-fileupload:Apache的文件上传组件,取代原生的文件上传流。
  • commons-io:commons-fileupload组件依赖于该组件。

三、前端表单

  • form
    1. 提交方式:method=“post”(post传送的数据量大,可视为不受限制)
    2. 编码类型:enctype="multipart/form-data"(表单包含文件上传控件时必须使用)
  • input
    1. 类型:type=“file”
    2. 属性:带有name属性

四、实用类介绍

PS:一个表单项即一个字段,一个FileItem对象封装了一个表单项

  • DiskFileItemFactory类
// 1、设置临时文件夹
void setRepository(File repository)

// *2、设置文件缓存区大小
void setSizeThreshold(int sizeThreshold)
  • ServletFileUpload类
// 1、静态方法:判断表单是否包含文件上传控件,负责处理文件数据
static boolean isMultipartContent(HttpServletRequest request)
    
// 2、父类方法:设置FileItemFactory属性,也可通过构造方法设置
void setFileItemFactory(FileItemFactory factory)
   
// 3、解析前端请求,将每个表单项解析并封装成FileItem对象,以List列表的形式返回。
List<FileItem> parseRequest(HttpServletRequest request)
    
// *4、父类方法:监听文件上传进度
void setProgressListener(ProgressListener pListener)
    
// *5、父类方法:处理乱码问题
void setHeaderEncoding(String encoding)
    
// *6、父类方法:设置单个文件的最大值
void setFileSizeMax(long fileSizeMax)
    
// *7、父类方法:设置总共能够上传的文件大小
void setSizeMax(long sizeMax)
  • FileItem接口实现类:DiskFileItem
// 1、判断表单项是否为上传文件:普通文本返回true,否则返回false
boolean isFormField();

// 2、获取字段名:表单项name属性的值
String getFieldName();

// 3、FileItem对象中保存的数据流内容:即表单项为普通文本的输入值
String getString();

// 4、获取表单项为文件上传的文件名
String getName();

// 5、获取上传文件的输入流
InputStream getInputStream();

// 6、清空FileItem类对象中存放的主体内容,如果主体内容被保存在临时文件中,delete方法将删除该临时文件
void delete();

五、代码实现

1、导入Maven依赖

(普通项目则导入jar包)

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.3</version>
</dependency>

2、前端页面

<h1>${msg}</h1>
<form action="${pageContext.request.contextPath}/upload.do" method="post" enctype="multipart/form-data">
    <p>上传用户:<input type="text" name="username"></p>
    <p><input type="file" name="file"></p>
    <p>
        <input type="submit">|<input type="reset">
    </p>
</form>

3、Servlet

思路

  1. 判断表单是否包含文件上传控件。
  2. 创建上传文件和临时文件的保存路径。
  3. 创建DiskFileItemFactory对象。
    • 设置临时文件夹
    • *设置缓冲区大小
  4. 创建ServletFileUpload对象
    • 设置FileItemFactory
    • *监听文件上传进度、处理乱码问题、设置单个文件和总共上传文件的最大值
  5. 解析请求并处理文件传输
    • 解析前端请求,将每个表单项解析并封装成FileItem对象
    • 判断表单项是否为上传文件
    • 处理普通文本:
      • 获取字段名
      • 获取数据流内容
    • 处理文件:
      • 获取上传文件名
      • 生成随机UUID作为文件存储路径,并为其生成文件夹
      • 通过IO流传输文件
public class FileServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        // 判断表单是否包含文件上传控件
        if (!ServletFileUpload.isMultipartContent(req)) { // 不包含文件上传控件,即普通表单
            return;
        }

        // 创建上传文件的保存路径:外界无法直接访问
        String uploadPath = this.getServletContext().getRealPath("/WEB-INF/upload");
        File uploadFile = new File(uploadPath);
        makeDirIfNotExist(uploadFile);

        // 创建临时文件的保存路径
        String tmpPath = this.getServletContext().getRealPath("/WEB-INF/tmp");
        File tmpFile = new File(tmpPath);
        makeDirIfNotExist(tmpFile);


        // 1、创建DiskFileItemFactory对象
        DiskFileItemFactory factory = getDiskFileItemFactory(tmpFile);
        // 2、创建ServletFileUpload对象
        ServletFileUpload servletFileUpload = getServletFileUpload(factory);
        // 3、解析请求并处理文件传输
        String msg = "";
        try {
            msg = uploadParseRequest(req, servletFileUpload, uploadPath);
        } catch (FileUploadException e) {
            e.printStackTrace();
        }

        req.setAttribute("msg", msg);
        req.getRequestDispatcher("/index.jsp").forward(req, resp);
    }


    private String uploadParseRequest(HttpServletRequest httpServletRequest, ServletFileUpload servletFileUpload, String uploadPath)
            throws FileUploadException, IOException {
        String msg = "";

        // 解析前端请求,将每个表单项解析并封装成FileItem对象
        List<FileItem> fileItems = servletFileUpload.parseRequest(httpServletRequest);
        for (FileItem fileItem : fileItems) {
            // 判断表单项是否为上传文件
            if (fileItem.isFormField()) { // 普通文本
                String filedName = fileItem.getFieldName(); // 获取字段名:表单项name属性的值
                String value = fileItem.getString("UTF-8"); // FileItem对象中保存的数据流内容:即表单项为普通文本的输入值
                System.out.println(filedName + ":" + value);
            } else { // 上传文件
                // ------------------------1、处理文件------------------------


                String uploadFileName = fileItem.getName();// 上传文件名
                System.out.println("上传的文件名:" + uploadFileName);

                if (uploadFileName == null || uploadFileName.trim().equals("")) { // 文件名为空值或空
                    continue; // 进入下一轮循环,判断下一个FileItem对象
                }

                String fileExtName = uploadFileName.substring(uploadFileName.lastIndexOf(".") + 1); // 文件后缀名
                System.out.println("文件信息【文件名:" + uploadFileName + ",文件类型:" + fileExtName + "】");

                // 使用UUID保证文件名唯一
                String uuidPath = UUID.randomUUID().toString();

                // ------------------------2、处理路径------------------------

                // 存储路径:uploadPath
                String realPath = uploadPath + '/' + uuidPath; // 真实存在的路径
                // 给每个文件创建一个文件夹
                File realPathFile = new File(realPath);
                makeDirIfNotExist(realPathFile);

                // ------------------------3、文件传输------------------------

                // 获得输入流
                InputStream is = fileItem.getInputStream();
                // 获得输出流
                FileOutputStream fos = new FileOutputStream(realPathFile + "/" + uploadFileName);
                // 创建缓冲区
                byte[] buffer = new byte[1024];

                int len;
                while ((len = is.read(buffer)) > 0) {
                    fos.write(buffer, 0, len);
                }
                msg = "文件上传成功!";

                fileItem.delete(); // 上传成功,清除临时文件

                fos.close();
                is.close();
            }
        }

        return msg;
    }


    private ServletFileUpload getServletFileUpload(DiskFileItemFactory factory) {
        // ServletFileUpload servletFileUpload = new ServletFileUpload(factory); // 构造方法设置FileItemFactory

        ServletFileUpload servletFileUpload = new ServletFileUpload();
        servletFileUpload.setFileItemFactory(factory); // 设置FileItemFactory

        // ------------------------辅助功能------------------------
        // 监听文件上传进度
        servletFileUpload.setProgressListener(new ProgressListener() {
            /**
             *
             * @param pBytesRead      已读取的文件大小
             * @param pContentLength  文件大小
             * @param pItems
             */
            @Override
            public void update(long pBytesRead, long pContentLength, int pItems) {
                String percentage = (int) (((double) pBytesRead / pContentLength) * 100) + "%";
                System.out.println("总大小:" + pContentLength + ",已上传:" + pBytesRead + "\t" + percentage);
            }
        });
        // 处理乱码问题
        servletFileUpload.setHeaderEncoding("UTF-8");
        // 设置单个上传文件的最大值
        servletFileUpload.setFileSizeMax(1024 * 1024 * 10); // 10M
        // 设置总共上传文件的最大值
        servletFileUpload.setSizeMax(1024 * 1024 * 10); // 10M

        return servletFileUpload;
    }

    /**
     * 获得磁盘文件项目工程,设置缓冲文件夹及缓冲区大小
     *
     * @param tmpFile 缓冲文件夹
     * @return
     */
    private DiskFileItemFactory getDiskFileItemFactory(File tmpFile) {
        // return new DiskFileItemFactory(1024 * 1024, tmpFile);

        DiskFileItemFactory factory = new DiskFileItemFactory();

        // ------------------------辅助功能------------------------
        factory.setSizeThreshold(1024 * 1024); // 1M(缓冲区大小):上传文件大于缓冲区大小时,fileupload组件将使用临时文件缓存上传文件
        factory.setRepository(tmpFile); // 临时文件夹
        return factory;
    }

    /**
     * 如果文件目录不存在,为其创建目录
     *
     * @param file
     */
    private void makeDirIfNotExist(File file) {
        if (!file.exists()) {
            file.mkdir();
        }
    }
}

4、注册Servlet

<servlet>
    <servlet-name>FileServlet</servlet-name>
    <servlet-class>indi.jaywee.file.FileServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>FileServlet</servlet-name>
    <url-pattern>/upload.do</url-pattern>
</servlet-mapping>

5、运行结果

Java Web 文件上传

上传的文件会保存在Target目录下对应项目的保存路径下。

Java Web 文件上传

  • 非临时文件:

    • 保存在upload文件夹下。
    • 文件格式和文件名与上传文件完全相同。
    • 每个文件存储在一个子文件夹中,文件夹名是随机生成的UUID。

    Java Web 文件上传

  • 临时文件:

    • 保存在upload文件夹下,同时在tmp文件夹下生成一个临时文件。
    • upload文件夹下的文件:文件格式和文件名与上传文件完全相同。
    • tmp文件夹下的文件:tmp格式,文件名是随机生成的UUID,在Servlet运行到fileItem.delete()方法时被清除。

    Java Web 文件上传

上一篇:Java Web文件上传(感谢秦疆老师)


下一篇:javaweb 文件下载和上传