Java程序员从笨鸟到菜鸟(二十六)servlet实现文件上传和下载
程序员文章站
2022-05-30 21:25:57
...
目录
- 一、所需的jar包
- 二、文件上传
-
文件上传都是以流的方式提交服务器端的,如果直接使用servlet获取上传文件的输入流然后再解析里面的请求参数是比较麻烦的,一般采用apache的common-fileupload组件
一、所需要的jar包 |
- common-io
- common-fileupload
- jstl
二、文件上传 |
2.1 上传文件步骤:
- 获取文件保存目录
- 判断目录是否存在,不存在就新建
- 创建DiskFileItemFactory工厂
- 创建文件解析器ServletFileUpload
- 判断提交的数据是否是上传的表单数据
- 使用ServletFileUpload解析上传数据,结果返回一个List集合,每一个FileItem对应一个Form表单输入项
- 遍历集合,判断输入的是否是上传文件
- 获得上传文件名称,只保留文件名部分
- 获取item中的文件输入流InputStream in
- 创建一个文件输出流FileOutputStream out
- 创建一个缓冲区byte[] buffer
- 判断输入流中的数据是否读完 len = in.read(buffer) > 0
- 使用FileOutputStream输出流将数据写入指定目录
- 关闭输入流、输出流
2.2 文件上传页面:upload.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>测试文件上传</title>
<script type="text/javascript" src="../../resources/js/jquery.min.js"></script>
</head>
<body>
<form action="${pageContext.request.contextPath }/servlet/UploadServlet" enctype="multipart/form-data" method="post">
上传文件:<input type="file" name="file"><br/>
<input type="submit" value="上传"/>文件上传结果:${message}
</form>
<script type="text/javascript">
// 触发文件选择的click事件
$("#file").trigger("click");
</script>
</body>
</html>
2.3 处理文件上传的servlet:UploadServlet
在web.xml中配置servlet
<!-- 实现文件上传 -->
<servlet>
<servlet-name>UploadServlet</servlet-name>
<servlet-class>ssh.controller.UploadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UploadServlet</servlet-name>
<url-pattern>/servlet/UploadServlet</url-pattern>
</servlet-mapping>
UploadServlet 实现:
public class UploadServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置文件上传保存的目录
String savePath = this.getServletContext().getRealPath("/WEB-INF/upload");
File file = new File(savePath);
if (!(file.exists()) && file.isDirectory()) {
file.mkdir();
}
// 上传时生成的临时问价保存目录
String tempPath = this.getServletContext().getRealPath("/WEB-INF/temp");
File tempFile = new File(tempPath);
if (!tempFile.exists()) { // 存放临时目录的文件夹不存在,新建目录
tempFile.mkdir();
}
// 消息提示
String message = "";
try {
// 1.创建一个DiskFileItemFactory工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
// 设置工厂的缓冲区大小,当上传文件超过缓冲区大小,就会创建一个临时文件存放到指定的临时目录
factory.setSizeThreshold(1024 * 100); // 设置缓冲区的大小为100KB,默认大小是10KB
// 设置上传时生成的临时文件保存目录
factory.setRepository(tempFile);
// 2.创建一个文件上传解析器
ServletFileUpload upload = new ServletFileUpload(factory);
// 解决上传文件中文乱码
upload.setHeaderEncoding("utf-8");
// 设置上传单个文件的大小最大值,设置1024 * 1024字节,也就是1MB
upload.setFileSizeMax(1024 * 1024 * 50);
// 设置同时上传各个文件大小总和不能超过10MB
upload.setSizeMax(1024 * 1024 * 500);
// 3.判断提交上来的数据是否是上传表单的数据
if (!ServletFileUpload.isMultipartContent(request)) {
return;
}
// 4.使用ServletFilterUpload解析器解析上传数据,解析结果返回的是一个List<FileItem>集合,每一个FileItem代表一个表单输入项
List<FileItem> list = upload.parseRequest(request);
for (FileItem item: list) {
// 如果fileItem封装的是普通的输入项的数据
if (item.isFormField()) {
String name = item.getFieldName();
// 解决普通输入项的数据的中文乱码问题
String value = item.getString("utf-8");
} else { // 如果fileItem封装的是上传的文件
// 获取上传文件的名称
String fileName = item.getName();
if (null == fileName || "".equals(fileName.trim())) {
continue;
}
// 处理获取到的上传文件名的路径部分,只保留文件名部分
fileName = fileName.substring(fileName.lastIndexOf("\\") + 1);
// 得到文件的扩展名
String fileExtrName = fileName.substring(fileName.lastIndexOf(".") + 1);
// 如果需要限制上传文件的格式,可以根据扩展名做过滤
if (!("txt".equals(fileExtrName))) {
message = "只能上传文本文档";
//request.setAttribute("message", "只能上传文本文档");
//request.getRequestDispatcher("/WEB-INF/jsp/message.jsp").forward(request, response);
} else {
// 监听文件上传进度
upload.setProgressListener(new ProgressListener() {
@Override
public void update(long l, long l1, int i) {
System.out.println("文件大小为:" + l1 + ",当前已处理:" + l);
}
});
// 获取item中的上传文件的输入流
InputStream in = item.getInputStream();
// 创建一个文件输出流
FileOutputStream out = new FileOutputStream(savePath + "\\" + fileName);
// 创建一个缓冲区
byte[] buffer = new byte[1024];
// 判断输入流是否读完的标志
int len = 0;
// 循环将输入流读到缓冲区
while ((len = in.read(buffer)) > 0) {
// 使用输入流将缓冲区的数据写入指定的保存目录savePath
out.write(buffer, 0, len);
}
// 关闭输入流
in.close();
// 关闭输出流
out.close();
// 删除处理文件上传时生成的临时文件
item.delete();
message = "文件上传成功";
}
}
}
} catch (FileUploadBase.FileSizeLimitExceededException e) {
e.printStackTrace();
message = "单个文件大小不能超过50MB";
} catch (FileUploadBase.SizeLimitExceededException e) {
e.printStackTrace();
message = "上传的文件总数大小超限";
} catch (Exception e) {
e.printStackTrace();
message = "文件上传失败";
} finally {
request.setAttribute("message", message);
request.getRequestDispatcher("/WEB-INF/upload/upload.jsp").forward(request, response);
}
}
/**
* 生成文件文件名,文件名以:uuid+"_"+文件名称
* @param fileName 文件名称
* @return uuid + "_" + 文件名称
* */
private String createFileName(String fileName) {
// 为防止文件覆盖的现象发生,为上传的文件产生一个唯一的文件名
return UUID.randomUUID().toString() + "_" + fileName;
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
运行效果:
文件上传可以对上传文件的格式(获取扩展名)、文件大小、单次上传文件的总大小做限制,代码中有注释体现
三、文件下载 |
3.1 可供下载的资源文件列表显示
- 获取上传文件的目录
- 存储要下载的文件名Map
- 遍历递归所有的文件和目录,将文件存储到map中
- 将Map结合发送到前台页面进行显示
显示文件列表的jsp页面:listfile.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<title>下载文件显示页面</title>
</head>
<body>
<table border="1">
<!-- 遍历Map集合 -->
<c:forEach var="file" items="${fileNameMap}">
<tr>
<c:url value="/servlet/DownloadServlet" var="downurl">
<c:param name="filename" value="${file.key}"/>
</c:url>
<td> ${file.value}<a href="${downurl}">下载</a></td>
</tr>
</c:forEach>
</table>
</body>
</html>
处理显示文件的servlet:FileListServlet
在web.xml中配置
<!-- 列出文件下载目录 -->
<servlet>
<servlet-name>FileListServlet</servlet-name>
<servlet-class>ssh.controller.FileListServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FileListServlet</servlet-name>
<url-pattern>/servlet/FileListServlet</url-pattern>
</servlet-mapping>
FileListServlet实现:
public class FileListServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取文件上传目录
String uploadFilePath = this.getServletContext().getRealPath("/WEB-INF/upload");
// 存储要下载的文件名
Map<String, String> fileNameMap = new HashMap<String, String>();
// 递归遍历filePath目录下所有的文件和目录,将文件名存储到map集合中
listFile(new File(uploadFilePath), fileNameMap);
// 将map集合发送到listfile.jsp进行页面展示
request.setAttribute("fileNameMap", fileNameMap);
request.getRequestDispatcher("/WEB-INF/jsp/listfile.jsp").forward(request, response);
}
/**
* 递归遍历指定目录下所有文件
* @param file 代表一个文件,也代表一个目录
* @param map 用来存储文件名
*
* */
private void listFile(File file, Map<String, String> map) {
// 如果file代表的不是一个文件,而是一个目录
if (!file.isFile()) {
// 列出文件夹下所有的文件和目录
File[] files = file.listFiles();
// 遍历File数组
for (File f: files) {
// 递归
listFile(f, map);
}
} else {
// 处理文件名,上传后的文件是以uuid_文件名的形式重新命名的
String realFileName = file.getName().substring(file.getName().indexOf("_") + 1);
map.put(file.getName(), realFileName);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
文件列表显示效果:
3.2 文件下载
- 获取需要下载的文件名
- 通过文件名找出文件所在的位置
- 得到要下载的文件,如果文件不存在信息提示
- 读取要下载的文件,保存到文件输入流
- 创建输出流
- 创建缓冲区
- 循环将输入流中的内容读到缓冲区
- 关闭输入流、输出流
处理文件下载的DownloadServlet
在web.xml中配置
<!-- 实现文件下载 -->
<servlet>
<servlet-name>DownloadServlet</servlet-name>
<servlet-class>ssh.controller.DownloadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DownloadServlet</servlet-name>
<url-pattern>/servlet/DownloadServlet</url-pattern>
</servlet-mapping>
DownloadServlet实现
public class DownloadServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取要下载的文件名
String fileName = request.getParameter("filename");
// 上传文件保存的目录
String fileSavePath = this.getServletContext().getRealPath("/WEB-INF/upload");
// 通过文件名找出文件所在目录
String path = findFileSavePathByName(fileName, fileSavePath);
// 得到要下载的文件
File file = new File(path + "\\" + fileName);
// 如果文件不存在
if (!file.exists()) {
request.setAttribute("message", "您要下载的资源已被删除");
request.getRequestDispatcher("/WEB-INF/jsp/message.jsp").forward(request, response);
}
// 处理文件名
String realName = fileName.substring(fileName.indexOf("_") + 1);
// 设置响应头,控制浏览器下载该文件
response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(realName, "UTF-8"));
// 读取要下载的文件,保存到文件输入流
FileInputStream fis = new FileInputStream(path + "\\" + fileName);
// 创建输出流
OutputStream os = response.getOutputStream();
// 创建缓冲区
byte[] bytes = new byte[1024];
int len = 0;
// 循环将输入流中的内容读取到缓冲区
while ((len = fis.read(bytes)) > 0) {
os.write(bytes, 0, len);
}
// 关闭文件输入流
fis.close();
// 关闭文件输出流
os.close();
}
/**
* 通过文件名找出文件所在的路径
* @param fileName 要下载的文件名
* @param fileSavePath 上传文件的根目录
* @return 返回要下载文件的存储目录
* */
private String findFileSavePathByName(String fileName, String fileSavePath) {
int hashcode = fileName.hashCode();
//int dir1 = hashcode & 0xf; // 使用hascode打散存储,但是文件目录获取之后出错
//int dir2 = (hashcode & 0xf0) >> 4;
String dir = fileSavePath;
File file = new File(dir);
if (!file.exists()) {
// 创建目录
file.mkdir();
}
return dir;
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
运行结果: