基于Servlet4的文件上传下载功能,原生态。
程序员文章站
2022-05-30 22:12:23
...
效果展现
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,不过当你需要制作进度条的时候这两个包有用的。
结构
上传表单
<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,它有如下几个字段
- 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算法进行打散存储,还有一些细节等。但是实际当中我们还需要对上传的文件的后缀名进行筛选,这个还是挺简单的。以上就是文件简单上传的全部内容咯~
上一篇: 在Dialog中实现下拉框效果并对下拉框赋自定义的值
下一篇: 自定义流式布局FlowLayout
推荐阅读
-
AngularJS基于http请求实现下载php生成的excel文件功能示例
-
基于jquery的tableExportJs的导出文件(附带打印功能)
-
IPFS - 基于内容寻址的, 具有版本控制功能的, P2P文件系统(翻译)
-
基于Java的一个简单的文件上传下载功能
-
基于vue-simple-uploader封装文件分片上传、秒传及断点续传的全局上传插件功能
-
基于bootstrap的上传插件fileinput实现ajax异步上传功能(支持多文件上传预览拖拽)
-
基于Servlet4的文件上传下载功能,原生态。
-
Java读取文件以及获取电话号码功能(基于正则表达式)的详解
-
AngularJS基于http请求实现下载php生成的excel文件功能示例
-
Java读取文件以及获取电话号码功能(基于正则表达式)的详解