一、概述
-
实现功能的类
- ServletFileUpload
- 负责处理上传的文件数据,并将表单中每个输入项封装成一个FileItem对象, 在使用ServletFileUpload对象解析请求时需要DiskFileItemFactory对象。所以,我们需要在进行解析工作前构造好DiskFileItemFactory对象,通过ServletFileUpload对象的构造方法或setFileItemFactory()方法设置ServletFileUpload对象的fileItemFactory属性。
- DiskFileItemFactory
- 将请求消息实体中的每一个项目封装成单独的DiskFileItem (FileItem接口的实现) 对象的任务由 FileItemFactory 接口的默认实现DiskFileItemFactory 来完成。当上传的文件项目比较小时,直接保存在内存中(速度比较快),比较大时,以临时文件的形式,保存在磁盘临时文件夹(虽然速度慢些,但是内存资源是有限的)。
- ServletFileUpload
-
项目所需依赖
- jar包下载链接
- 使用Maven管理
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency>
二、实现功能的注意事项
- 为保证服务器安全,上传文件应放在外界无法直接访问的目录下,就比如放于WEB-INF目录下
- 为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名
- 要限制上传文件的最大值,避免用户恶意占用磁盘资源
- 要限制上传文件的类型,在收到上传文件时,判断后缀名是否合法
- 表单 method 属性应该设置为POST 方法,不能使用 GET 方法
三、代码实现
- index.jsp
${pageContext.request.contextPath}
是JSP取得绝对路径的方法,等价于<%=request.getContextPath()%>
,也就是取出部署的应用程序名或者是当前的项目名称。enctype="multipart/form-data"
是将文件以二进制的形式上传,这样可以实现多种类型的文件上传。- 表单必须是POST请求。
<%-- 这里主要讲解后端实现,前端就不过多展示 --%>
<form action="${pageContext.request.contextPath}/upload" enctype="multipart/form-data" method="post">
<input type="text" name="content">
<input type="file" name="file01">
<input type="file" name="file02">
<input type="submit" value="提交" />
</form>
- web.xml
- 这里我用的是web.xml配置Servlet,小伙伴也可以选择使用注解
@WebServlet
的方式配置Servlet,详情可见@webServlet注解的使用(适合初次接触的朋友)
- 这里我用的是web.xml配置Servlet,小伙伴也可以选择使用注解
<!--文件上传-->
<servlet>
<servlet-name>UploadServlet</servlet-name>
<servlet-class>com.hello.UploadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UploadServlet</servlet-name>
<url-pattern>/upload</url-pattern>
</servlet-mapping>
- Servlet
package com.hello;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;
/**
* 文件上传功能
*/
@SuppressWarnings({"all"})
public class UploadServlet extends HttpServlet {
@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// ================ 初始化 ===================
// 接收和响应的编码处理
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
// 创建文件的上传路径
String uploadPath = req.getServletContext().getRealPath("/WEB-INF/upload");
System.out.println("realPath:" + uploadPath);
File uploadFile = new File(uploadPath);
// 文件夹不存在,需要创建文件夹
if (!uploadFile.exists())
uploadFile.mkdir();
// 创建大文件临时保存路径,重复上述步骤
String tempPath = req.getServletContext().getRealPath("/WEB-INF/temp");
System.out.println("realPath:" + tempPath);
File tempFile = new File(tempPath);
if (!tempFile.exists())
tempFile.mkdir();
// 创建 DiskFileItemFactory 对象
DiskFileItemFactory factory = new DiskFileItemFactory();
// 设置一个缓冲区,当上传的文件大于这个缓冲区的时候,将他放到临时文件中
factory.setSizeThreshold(1024 * 1024 * 10); // 缓冲区大小设为 10M
factory.setRepository(tempFile); // 临时文件保存的目录,需要一个FiLe
// 创建 ServletFileUpload对象
ServletFileUpload upload = new ServletFileUpload(factory);
// 设置文件编码
upload.setHeaderEncoding("utf-8");
// 设置单文件接收的最大值 10M
upload.setFileSizeMax(1024 * 1024 * 10);
// 设置总共能够上传的文件大小 10M
upload.setSizeMax(1024 * 1024 * 10);
// 监听文件的上传进度
upload.setProgressListener(new ProgressListener() {
public void update(long pBytesRead, long pContentLength, int pItems) {
// pBytesRead:已经读取到的文件大小
// pContentLength:文件大小
System.out.println("文件总大小:" + pContentLength + "已上传" + pBytesRead);
}
});
/*============== 处理请求 ==============*/
try {
// 将前端的请求解析成 fileItem对象
List<FileItem> fileItems = upload.parseRequest(req);
// 遍历List集合,判断是否为文件
for (FileItem fileItem : fileItems) {
if (fileItem.isFormField()) {
// 普通表单控件
String name = fileItem.getFieldName();
String value = fileItem.getString("utf-8");
System.out.println(name + ":" + value);
} else {
// ================ 文件处理开始 ===================
// 获取文件名字,判断文件名字是否合法
String fileName = fileItem.getName();
if (fileName.trim().equals("") && fileName == null)
continue;
// 获取上传的文件名和文件后缀名
String uploadFileName = fileName.substring(fileName.lastIndexOf("/") + 1);
String uploadFileEndName = fileName.substring(fileName.lastIndexOf(".") + 1);
// 这里可以验证文件的后缀名是否为所需...
System.out.println("文件:" + uploadFileName + "正在上传,文件类型为" + uploadFileEndName);
// 这里为了文件名的唯一性,可以使用UUID(唯一识别通用码)来保证唯一性,关于UUID的用法,这里不再赘述,有兴趣的小伙伴可以去了解一下
String uuidPath = UUID.randomUUID().toString();
// ================ 文件处理结束 ===================
// ================ 文件传输开始 ===================
// 获取输入流
InputStream in = fileItem.getInputStream();
// 获取文件输出流,路径为存放文件的真实路径 /WEB-INF/xxx
FileOutputStream out = new FileOutputStream(uploadPath + "/" + uuidPath + "." + uploadFileEndName);
// 创建缓冲区
byte[] buffer = new byte[1024];
int len = 0;
// 将流写入文件
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
System.out.println("文件上传成功,路径为:" + uploadPath + "\\" + uploadFileName);
// 最后关闭流,释放资源
out.close();
in.close();
// 响应给前端文件上传成功的信息
req.setAttribute("msg", "文件上传成功");
// ================ 文件传输结束 ===================
}
}
} catch (Exception e) {
// 响应给前端文件上传失败的信息
req.setAttribute("msg", "文件上传失败");
throw new RuntimeException(e);
}
}
}
四、最终效果图
- 到这里功能就基本实现了,如果有报错的话,就需要去检查一下代码是否打错或者看一下配置是否正确了