Servlet 可以与 HTML form 标签一起使用,来允许用户上传文件到服务器。上传的文件可以是文本文件、图像文件或任何文档。

创建一个文件上传表单

下面的 JSP页面创建了一个文件上传表单。以下几点需要注意:

  • 表单 method 属性应该设置为 POST 方法,不能使用 GET 方法。
  • 表单 enctype 属性应该设置为 multipart/form-data.
  • 表单 action 属性应该设置为在后端服务器上处理文件上传的 Servlet 文件。

    1. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    2. <!DOCTYPE html>
    3. <html lang="zh">
    4. <html>
    5. <head>
    6. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    7. <title>文件上传实例</title>
    8. </head>
    9. <body>
    10. <h1>文件上传实例</h1>
    11. <form method="post" action="/crm/upLoadFileServlet" enctype="multipart/form-data">
    12. 头像上传:
    13. <input type="file" name="uploadFile" />
    14. <br/><br/>
    15. 名字:
    16. <input type="text" name="name" />
    17. <br/><br/>
    18. 年龄:
    19. <input type="text" name="age" />
    20. <br/><br/>
    21. <input type="submit" value="上传" />
    22. </form>
    23. </body>
    24. </html>

    编写后台 Servlet

    不能直接通过下面的方式获取文件上传参数(HTTP协议有关,这是一个庞大的话题,可以先理解如果上传文件,上传文件参数格式会不一样,需要特殊处理解析)

    1. request.getParameter("name"); //只能解析name="lff"&age=18&height=175这种格式的请求参数

    上面只能获取常规的get、post请求参数。如果是文件上传的参数我们通常使用第三方库Commons FileUpload来解析参数,使用Maven方式依赖如下

    1. <!-- 解析文件上传参数 -->
    2. <dependency>
    3. <groupId>commons-fileupload</groupId>
    4. <artifactId>commons-fileupload</artifactId>
    5. <version>1.4</version>
    6. </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

  1. package com.lff.crm.servlet;
  2. import org.apache.commons.fileupload.FileItem;
  3. import org.apache.commons.fileupload.disk.DiskFileItemFactory;
  4. import org.apache.commons.fileupload.servlet.ServletFileUpload;
  5. import org.apache.commons.io.FilenameUtils;
  6. import javax.servlet.*;
  7. import javax.servlet.http.*;
  8. import javax.servlet.annotation.*;
  9. import java.io.File;
  10. import java.io.IOException;
  11. import java.util.List;
  12. import java.util.UUID;
  13. @WebServlet("/upLoadFileServlet")
  14. public class upLoadFileServlet extends HttpServlet {
  15. // 上传文件存储目录
  16. private static final String UPLOAD_DIRECTORY = "upload";
  17. // 上传配置
  18. private static final int MEMORY_THRESHOLD = 1024 * 1024 * 3; // 3MB
  19. private static final int MAX_FILE_SIZE = 1024 * 1024 * 40; // 40MB
  20. private static final int MAX_REQUEST_SIZE = 1024 * 1024 * 50; // 50MB
  21. @Override
  22. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  23. // 配置上传参数
  24. DiskFileItemFactory factory = new DiskFileItemFactory();
  25. // 设置内存临界值 - 超过后将产生临时文件并存储于临时目录中
  26. factory.setSizeThreshold(MEMORY_THRESHOLD);
  27. // 设置临时存储目录
  28. factory.setRepository(new File(System.getProperty("java.io.tmpdir")));
  29. ServletFileUpload upload = new ServletFileUpload(factory);
  30. // 设置最大文件上传值
  31. upload.setFileSizeMax(MAX_FILE_SIZE);
  32. // 设置最大请求值 (包含文件和表单数据)
  33. upload.setSizeMax(MAX_REQUEST_SIZE);
  34. // 中文处理,防止乱码
  35. upload.setHeaderEncoding("UTF-8");
  36. // 构造临时路径来存储上传的文件
  37. // 这个路径相对当前应用的目录
  38. String uploadPath = request.getServletContext().getRealPath("./") + UPLOAD_DIRECTORY;
  39. // 如果目录不存在则创建
  40. File uploadDir = new File(uploadPath);
  41. if (!uploadDir.exists()) {
  42. uploadDir.mkdir();
  43. }
  44. try {
  45. // 解析请求的内容提取文件数据,一个FileItem代表一个请求参数包括文件参数和非文件参数
  46. @SuppressWarnings("unchecked")
  47. List<FileItem> formItems = upload.parseRequest(request);
  48. if (formItems != null && formItems.size() > 0) {
  49. // 迭代表单数据
  50. for (FileItem item : formItems) {
  51. if (item.isFormField()) { //非文件数据
  52. System.out.println("参数名称:" + item.getFieldName());
  53. System.out.println("参数值:" + item.getString());
  54. }else { //文件数据
  55. //重新生成唯一的文件名
  56. String fileName = UUID.randomUUID() + "." + FilenameUtils.getExtension(item.getName());
  57. String filePath = uploadPath + File.separator + fileName;
  58. File storeFile = new File(filePath);
  59. // 在控制台输出文件的上传路径
  60. System.out.println(filePath);
  61. // 保存文件到硬盘
  62. item.write(storeFile);
  63. }
  64. }
  65. }
  66. } catch (Exception ex) {
  67. System.out.println(ex.getMessage());
  68. }
  69. }
  70. }

文件应该保存在什么地方?

上面的例子中我们保存的路径是

  1. /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可以部署很多项目,我们通常把文件保存在对应的项目下面。

注意点

  • 保存在服务端磁盘中的文件名称最好要唯一,防止用户上传不同的文件,但是名称相同,这样就会把以前上传的文件覆盖掉,服务端最好重新生成一个,但是客户端上传文件的扩展名要保留。这就是为什么我们的书写的代码是这样的

    1. //重新生成唯一的文件名
    2. String fileName = UUID.randomUUID() + "." + FilenameUtils.getExtension(item.getName());
    3. String filePath = uploadPath + File.separator + fileName;
  • 在真实的项目中,保存文件到磁盘后,我们需要把保存后的文件路径存储到数据库中的,数据库中应该存储什么路径呢?想要知道应该存储什么路径,需要先了解客户端应该怎么访问上传后的文件。是这样访问的

    1. 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就可以了。