Servlet 可以与 HTML form 标签一起使用,来允许用户上传文件到服务器。上传的文件可以是文本文件、图像文件或任何文档。
创建一个文件上传表单
下面的 JSP页面创建了一个文件上传表单。以下几点需要注意:
- 表单 method 属性应该设置为 POST 方法,不能使用 GET 方法。
- 表单 enctype 属性应该设置为 multipart/form-data.
表单 action 属性应该设置为在后端服务器上处理文件上传的 Servlet 文件。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="zh">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>文件上传实例</title>
</head>
<body>
<h1>文件上传实例</h1>
<form method="post" action="/crm/upLoadFileServlet" enctype="multipart/form-data">
头像上传:
<input type="file" name="uploadFile" />
<br/><br/>
名字:
<input type="text" name="name" />
<br/><br/>
年龄:
<input type="text" name="age" />
<br/><br/>
<input type="submit" value="上传" />
</form>
</body>
</html>
编写后台 Servlet
不能直接通过下面的方式获取文件上传参数(HTTP协议有关,这是一个庞大的话题,可以先理解如果上传文件,上传文件参数格式会不一样,需要特殊处理解析)
request.getParameter("name"); //只能解析name="lff"&age=18&height=175这种格式的请求参数
上面只能获取常规的get、post请求参数。如果是文件上传的参数我们通常使用第三方库Commons FileUpload来解析参数,使用Maven方式依赖如下
<!-- 解析文件上传参数 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
如果不使用Maven,先确保依赖包已经引入到项目的 WEB-INF/lib 目录下:
确保在您的 classpath 中有最新版本的 commons-fileupload.x.x.jar 文件。可以从 http://commons.apache.org/proper/commons-fileupload/ 下载。
- FileUpload 依赖于 Commons IO,所以一定要确保在您的 classpath 中有最新版本的 commons-io-x.x.jar 文件。可以从 http://commons.apache.org/proper/commons-io/ 下载。
编写servlet
package com.lff.crm.servlet;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FilenameUtils;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.UUID;
@WebServlet("/upLoadFileServlet")
public class upLoadFileServlet extends HttpServlet {
// 上传文件存储目录
private static final String UPLOAD_DIRECTORY = "upload";
// 上传配置
private static final int MEMORY_THRESHOLD = 1024 * 1024 * 3; // 3MB
private static final int MAX_FILE_SIZE = 1024 * 1024 * 40; // 40MB
private static final int MAX_REQUEST_SIZE = 1024 * 1024 * 50; // 50MB
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 配置上传参数
DiskFileItemFactory factory = new DiskFileItemFactory();
// 设置内存临界值 - 超过后将产生临时文件并存储于临时目录中
factory.setSizeThreshold(MEMORY_THRESHOLD);
// 设置临时存储目录
factory.setRepository(new File(System.getProperty("java.io.tmpdir")));
ServletFileUpload upload = new ServletFileUpload(factory);
// 设置最大文件上传值
upload.setFileSizeMax(MAX_FILE_SIZE);
// 设置最大请求值 (包含文件和表单数据)
upload.setSizeMax(MAX_REQUEST_SIZE);
// 中文处理,防止乱码
upload.setHeaderEncoding("UTF-8");
// 构造临时路径来存储上传的文件
// 这个路径相对当前应用的目录
String uploadPath = request.getServletContext().getRealPath("./") + UPLOAD_DIRECTORY;
// 如果目录不存在则创建
File uploadDir = new File(uploadPath);
if (!uploadDir.exists()) {
uploadDir.mkdir();
}
try {
// 解析请求的内容提取文件数据,一个FileItem代表一个请求参数包括文件参数和非文件参数
@SuppressWarnings("unchecked")
List<FileItem> formItems = upload.parseRequest(request);
if (formItems != null && formItems.size() > 0) {
// 迭代表单数据
for (FileItem item : formItems) {
if (item.isFormField()) { //非文件数据
System.out.println("参数名称:" + item.getFieldName());
System.out.println("参数值:" + item.getString());
}else { //文件数据
//重新生成唯一的文件名
String fileName = UUID.randomUUID() + "." + FilenameUtils.getExtension(item.getName());
String filePath = uploadPath + File.separator + fileName;
File storeFile = new File(filePath);
// 在控制台输出文件的上传路径
System.out.println(filePath);
// 保存文件到硬盘
item.write(storeFile);
}
}
}
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
}
}
文件应该保存在什么地方?
上面的例子中我们保存的路径是
/Users/fufali/Desktop/JAVA/Workspace/tomcat/apache-tomcat-9.0.54/webapps/crm/upload/
/Users/fufali/Desktop/JAVA/Workspace/tomcat/apache-tomcat-9.0.54为Tomcat的路径,我们是把项目部署在Tomcat的webapps下的,crm是我们的项目。Tomcat可以部署很多项目,我们通常把文件保存在对应的项目下面。
注意点
保存在服务端磁盘中的文件名称最好要唯一,防止用户上传不同的文件,但是名称相同,这样就会把以前上传的文件覆盖掉,服务端最好重新生成一个,但是客户端上传文件的扩展名要保留。这就是为什么我们的书写的代码是这样的
//重新生成唯一的文件名
String fileName = UUID.randomUUID() + "." + FilenameUtils.getExtension(item.getName());
String filePath = uploadPath + File.separator + fileName;
在真实的项目中,保存文件到磁盘后,我们需要把保存后的文件路径存储到数据库中的,数据库中应该存储什么路径呢?想要知道应该存储什么路径,需要先了解客户端应该怎么访问上传后的文件。是这样访问的
http://localhost:8080/crm/upload/a4292005-4ec1-4381-b54b-2e618c7ef895.png
http://localhost:8080为IP以及端口号
crm为项目也就是contentpath
这些都是会变动的,唯一不变的是upload/a4292005-4ec1-4381-b54b-2e618c7ef895.png这个路径。
我们应该把upload/a4292005-4ec1-4381-b54b-2e618c7ef895.png这个路径存到服务端,客户端想获取这个图片资源,只需前面拼接IP、端口以及contentpath就可以了。