欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

基于Servlet4的文件上传下载功能,原生态。

程序员文章站 2022-05-30 22:12:23
...

效果展现

基于Servlet4的文件上传下载功能,原生态。

maven依赖

为了让同学们直接引入依赖,我这里贴出了dependencies

  <dependencies>

    <!-- https://mvnrepository.com/artifact/jstl/jstl -->
    <dependency>
      <groupId>jstl</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>

    <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
      <scope>provided</scope>
    </dependency>

    <!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>javax.servlet.jsp-api</artifactId>
      <version>2.3.3</version>
      <scope>provided</scope>
    </dependency>

  </dependencies>

  PS:Servlet3之后,自带了文件上传功能,所以不需要用apache-commons-fileupload.jar+apache-commons-io.jar,不过当你需要制作进度条的时候这两个包有用的。

结构

基于Servlet4的文件上传下载功能,原生态。

上传表单

<form action="<%=basePath%>/FileUploadServlet" enctype="multipart/form-data" method="post">
    上传人:<input type="text" name="user">
    选择文件:
    <input type="file" name="upload">
    <input type="submit" value="上传">
</form>

  注意点一:enctype=”multipart/form-data”,这是告诉浏览器我是以二进制格式上传文件,这个字段就是设置MIME编码格式。日后你还可以通过它来上传图片什么的,不设置的话就只能上传文本这种表单格式。

工具类

package utils;

import java.io.File;
import java.util.Map;
import java.util.UUID;

public class FileUtils {
    /**
     * 根据hash打散文件,然后获取保存目录
     *
     * @param filename     文件名称
     * @param fileSaveRoot 文件保存根目录
     * @return 文件实际保存目录
     */
    public static String fileSave(String filename, String fileSaveRoot) {
        int hash = filename.hashCode();
        int dir1 = hash & 0xf;//0-15
        int dir2 = (hash & 0xf0) >> 4;//0-15

        String fileSavePath = fileSaveRoot + File.separator + dir1 + File.separator + dir2;
        System.out.println(fileSavePath);
        File file = new File(fileSavePath);
        if (!file.exists()) {
            //二级目录需要连续创建两个
            file.mkdirs();
        }
        return fileSavePath;
    }


    // 防止文件提交上来重复名字,所以加上唯一的UUID
    public static String makeFileName(String fileName) {
        return UUID.randomUUID().toString() + "_" + fileName;
    }


    /**
     *
     * 主要是为了之后展示给用户看,带上UUID不是很难受?
     * 
     * @param fileUUIDName UUID文件名
     * @return 截取之后的名字
     */
    public static String extractFileName(String fileUUIDName) {
        int index = fileUUIDName.indexOf("_");
        return fileUUIDName.substring(index + 1);
    }


    /**
     * 递归遍历文件树,将值存进map中便于jsp展示
     *
     * @param f 文件
     * @param map 存放文件的map,键是UUID名字,值是截取UUID后的
     */
    public static void putFiles(File f, Map<String, String> map) {
        File[] files = f.listFiles();
        for (File file : files) {
            if (file == null) {
                //回溯点
                return;
            }
            if (file.isDirectory()) {
                putFiles(file, map);
            } else {
                String fileUUIDName = file.getName();
                String fileName = extractFileName(fileUUIDName);
                map.put(fileUUIDName, fileName);
            }
        }
    }
}
  • 注意点二:当上传一个文件的时候,有可能文件名是一样的,为了保证文件名的唯一性需要加上UUID。
  • 注意点三:遍历文件目录,用的是递归,递归有两个要素,一:递归条件 二:回溯点。递归是一种天然的深度优先算法。如果你想控制遍历的深度,可以在方法后面加上一个int,作为每次递归的条件,这边将map作为每次递归进行传递,可以保证所有方法调用都是存的同一个map

文件上传

package web.controller;

import utils.FileUtils;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.UUID;

@WebServlet(name = "FileUploadServlet", urlPatterns = {"/FileUploadServlet"})
@MultipartConfig(maxFileSize = 1000 * 1024 * 1024,
        maxRequestSize = 1000 * 1024 * 1024)
public class FileUploadServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");
        String user = request.getParameter("user");
        System.out.println(user);

        String savePathRoot = this.getServletContext().getRealPath("/WEB-INF/upload");
        System.out.println(savePathRoot);

        Collection<Part> parts = request.getParts();
        for (Part part : parts) {

            /*Collection<String> headerNames = part.getHeaderNames();
            for (String name : headerNames) {
                System.out.println(name);
            }*/

            //获得提交的名字
            String filename = part.getSubmittedFileName();
            System.out.println(filename);

            //对于不是上传文件的input会传来空值,或者你没有指定上传的文件也是null,这里过滤一下
            if (filename == null) {
                continue;
            }
            String fileUUIDName = FileUtils.makeFileName(filename);
            String saveDir = FileUtils.fileSave(fileUUIDName, savePathRoot);
            /*long size = part.getSize();*/

            //File.separator是自适应的windows和linux分隔符就是\\和/
            part.write(saveDir + File.separator + fileUUIDName);
            //删除临时文件
            part.delete();
        }
    }
}

  Servlet3之后,可以使用注解了,这里介绍的是这个@MultipartConfig,它有如下几个字段
基于Servlet4的文件上传下载功能,原生态。

  • location:文件路径,一般都不在这个里面制定
  • maxFileSize:最大文件大小,单位字节。默认没有限制
  • maxRequestSize:一次request的大小,可以看做是接近所有上传文件的总和大小
  • fileSizeThreshold:文件阀值,默认为0,这个字段用于临时文件。如果大小>0,那么就将使用缓存文件。

通过这些限定,你可以使用异常来进行最大文件的限制然后提示用户不能超过多少M等。

展示页面

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String fileRoot = this.getServletContext().getRealPath("/WEB-INF/upload");
        File root = new File(fileRoot);
        if (!root.exists()) {
            return;
        }

        Map<String, String> fileMap = new HashMap<>();
        FileUtils.putFiles(root, fileMap);
        request.setAttribute("files", fileMap);
        request.getRequestDispatcher("listFiles.jsp").forward(request, response);
    }
<%@ page contentType="text/html;charset=UTF-8" isELIgnored="false" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<c:forEach var="map" items="${requestScope.files}">
    <c:url value="/DownLoadServlet" var="downloadUrl">
        <c:param name="fileName" value="${map.key}"/>
    </c:url>
    ${map.value}<a href="${downloadUrl}">立刻下载?</a>
</c:forEach>
</body>
</html>
  • 注意点四:用url标签是为了对中文url进行重写,不然就是乱码

下载

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //带UUID的文件名
        String fileUUIDName = request.getParameter("fileName");
        String fileName = FileUtils.extractFileName(fileUUIDName);
        System.out.println(fileName);

        String fileRoot = this.getServletContext().getRealPath("/WEB-INF/upload");

        //根据文件名找到存储目录
        String fileDir = FileUtils.fileSave(fileUUIDName, fileRoot);

        String targetFileUrl = fileDir + File.separator + fileUUIDName;
        System.out.println(targetFileUrl);
        File file = new File(targetFileUrl);

        if (!file.exists()) {
            System.out.println("目标文件不存在");
            return;
        }
        response.setHeader("content-disposition",
                "attachment;filename" + URLEncoder.encode(fileName, "UTF-8"));

        FileInputStream inputStream = new FileInputStream(file);
        ServletOutputStream servletOutputStream = response.getOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        while ((len = inputStream.read(buffer)) > 0) {
            servletOutputStream.write(buffer, 0, len);
        }
        inputStream.close();
        servletOutputStream.close();
    }

  下载很简单,读文件流进Servlet输出流里面进行输出即可。然后再写上头,注意的是这边要对文件名进行URL UTF-8编码,不然在浏览器控制台会显示?????。

总结

  这次学习了怎么遍历文件夹,然后hash算法进行打散存储,还有一些细节等。但是实际当中我们还需要对上传的文件的后缀名进行筛选,这个还是挺简单的。以上就是文件简单上传的全部内容咯~

相关标签: 文件下载上传