JavaWeb四种文件上传方式(上篇)

文件上传是 Java Web 开发必学核心知识点,日常项目、后台管理系统都高频用到。整体一共四种主流实现方式,分上下两篇:

上篇:Servlet2.5传统上传、Servlet3.0原生无依赖上传

下篇 :SpringMVC 两种文件上传写法(直接接收 MultipartFile、强转 MultipartHttpServletRequest)JavaWeb四种文件上传方式(下篇)-CSDN博客

环境:Tomcat 8.5 + JDK8 + Maven 所有代码可直接复制运行


一、Servlet2.5传统文件上传(commons 工具版)

1. 原理

早期 Servlet 没有内置文件上传功能,必须依赖 commons-fileupload + commons-io 两个第三方工具包。:

  • DiskFileItemFactory:磁盘文件工厂,设置内存阈值、临时缓存目录
  • ServletFileUpload:上传解析器,解析请求参数
  • FileItem:封装每一个表单字段,区分普通文本字段文件字段

流程:表单携带文件 → 解析请求 → 遍历表单项 → 提取文件名 → UUID 重命名 → 写入磁盘

2. Maven依赖

XML 复制代码
<dependencies>
    <!-- Servlet 原生依赖 -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
        <scope>provided</scope>
    </dependency>

    <!-- 文件上传核心依赖 -->
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.4</version>
    </dependency>
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.3.1</version>
    </dependency>
</dependencies>

3. 上传表单jsp

XML 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Servlet2.5 文件上传</title>
</head>
<body>
    <%-- 文件上传必须加 enctype="multipart/form-data" --%>
    <form action="/updateFile" method="post" enctype="multipart/form-data">
        文件选择:<input type="file" name="file"/><br>
        <input type="submit" value="提交上传">
    </form>
</body>
</html>

4. Servlet

java 复制代码
package com.qcby.servlet;

import org.apache.commons.fileupload.FileItem;
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.IOException;
import java.util.List;
import java.util.UUID;

public class FileServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=UTF-8");
        req.setCharacterEncoding("UTF-8");

        // 1. 判断当前请求是否为文件上传类型
        if (!ServletFileUpload.isMultipartContent(req)) {
            resp.getWriter().write("错误:表单不是文件上传类型!");
            return;
        }

        try {
            // 2. 创建磁盘工厂 + 上传解析器
            DiskFileItemFactory factory = new DiskFileItemFactory();
            ServletFileUpload upload = new ServletFileUpload(factory);

            // 3. 解析请求,获取所有表单项
            List<FileItem> fileItems = upload.parseRequest(req);

            // 4. 遍历表单项
            for (FileItem item : fileItems) {
                // 判断:不是普通文本字段 = 文件字段
                if (!item.isFormField() && "file".equals(item.getFieldName())) {
                    // 获取原始文件名
                    String originalFilename = item.getName();
                    // 截取文件后缀
                    String ext = originalFilename.substring(originalFilename.lastIndexOf("."));
                    // UUID 生成唯一文件名,防止重名覆盖
                    String newFileName = UUID.randomUUID().toString().replace("-","") + ext;

                    // 5. 定义保存目录,不存在则自动创建
                    File uploadDir = new File("D:/Aupload");
                    if (!uploadDir.exists()) {
                        uploadDir.mkdirs();
                    }

                    // 6. 保存文件到磁盘
                    File saveFile = new File(uploadDir, newFileName);
                    item.write(saveFile);

                    resp.getWriter().write("Servlet2.5 上传成功!新文件名:" + newFileName);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            resp.getWriter().write("上传失败:" + e.getMessage());
        }
    }
}

5. web.xml配置

XML 复制代码
<servlet>
    <servlet-name>fileServlet</servlet-name>
    <servlet-class>com.qcby.servlet.FileServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>fileServlet</servlet-name>
    <url-pattern>/updateFile</url-pattern>
</servlet-mapping>

6. 总结

  1. 依赖第三方 commons 包,不能脱离依赖单独使用
  2. 代码偏繁琐,需要手动区分普通字段和文件字段
  3. 兼容性老项目,现在新项目很少手写这套
  4. 必须表单指定 enctype="multipart/form-data"

二、Servlet3.0原生文件上传(无依赖版)

1. 原理

Servlet3.0 开始内置文件上传功能无需导入 commons-fileupload、commons-io 依赖。

  • @MultipartConfig:注解开启 Servlet3.0 上传支持
  • request.getPart("file"):直接获取上传文件对象
  • 不用手动解析表单项,API 更简洁

适合学习基础原理,不用引入额外依赖,代码极简。

2. Maven依赖

只需要原生 servlet 依赖即可:

XML 复制代码
<dependencies>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

3. 上传表单jsp

XML 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Servlet3.0 原生上传</title>
</head>
<body>
    <form action="upload3" method="post" enctype="multipart/form-data">
        选择文件:<input type="file" name="file"/><br>
        <input type="submit" value="原生上传提交">
    </form>
</body>
</html>

4. Servlet3.0完整代码

java 复制代码
package com.qcby.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.File;
import java.io.IOException;
import java.util.UUID;

// 路径注解配置
@WebServlet("/upload3")
// 开启文件上传功能
@MultipartConfig
public class Upload3Servlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");

        // 1. 根据表单name获取文件Part对象
        Part part = request.getPart("file");

        // 2. 解析请求头,提取原始文件名
        String header = part.getHeader("content-disposition");
        String fileName = header.substring(header.lastIndexOf("filename=") + 10);
        fileName = fileName.replace("\"","");

        // 3. 截取后缀 + UUID 重命名
        String suffix = fileName.substring(fileName.lastIndexOf("."));
        String newName = UUID.randomUUID().toString().replace("-","") + suffix;

        // 4. 定义保存目录,自动创建
        File saveDir = new File("D:/Aupload3");
        if(!saveDir.exists()){
            saveDir.mkdirs();
        }

        // 5. 写入磁盘
        part.write(saveDir + File.separator + newName);
        response.getWriter().write("Servlet3.0 原生上传成功!文件名:" + newName);
    }
}

5. 总结

  1. 零第三方依赖,仅 Servlet3.0 原生支持
  2. 注解开发:@WebServlet + @MultipartConfig,无需 web.xml
  3. API 简洁:getPart() 直接获取文件
  4. 适合学习底层原理,不依赖工具包
  5. 同样必须表单加 enctype="multipart/form-data"
相关推荐
网络工程小王1 小时前
【LangGraph的工作流编排能力】学习笔记
java·服务器·数据库·人工智能·langchain
j_xxx404_1 小时前
【Linux进程间通信】硬核剖析:消息队列、信号量、内核IPC资源统一管理与mmap加餐
linux·运维·开发语言·c++·人工智能·ai
她说可以呀1 小时前
JWT令牌检验用户是否登录
java·spring boot·spring·java-ee·maven
geovindu1 小时前
Python: Condition Variable Pattern
开发语言·python·设计模式·条件变量模式
故事和你911 小时前
蓝桥杯-2026年C++B组省赛
开发语言·数据结构·c++·算法·蓝桥杯·动态规划·图论
一氧化二氢.h1 小时前
【简单理解】数组、数组列表、集合
java
哆啦A梦15881 小时前
11,Springboot3+vue3个人中心,修改密码
java·前端·javascript·数据库·vue3
wjs20241 小时前
HTML DOM 属性
开发语言
小则又沐风a1 小时前
C++模板进阶
java·服务器·前端·c++