Java文件上传三方式对比:从传统到SpringBoot

目录

一、Java文件的三种上传方式

(一)本质区别

1.Servlet

[(1)传统 Servlet](#(1)传统 Servlet)

(2)原生Servlet3.0

2.传统SpringMVC

3.MultipartFile上传

(二)核心对比

(三)具体代码实现

1.传统Servlet

(1)目录

(2)pom.xml导入依赖

(3)编写测试页面index.jsp

(4)编写FileServlet

(5)编写web.xml

(6)配置tomcat后运行,上传文件

(7)点击提交后,查看本地文件夹是否有文件上传

[2.Spring MVC的方式](#2.Spring MVC的方式)

(1)编写pom.xml,导入依赖

2.原生Servlet3.0

(1)目录

[(2)导入依赖,因为 是原生tomcat自带的,所以只需导入这个tomcat的包](#(2)导入依赖,因为 是原生tomcat自带的,所以只需导入这个tomcat的包)

(3)编写测试页面index.jsp

(4)配置好tomcat,打开页面上传并提交文件,观察本地是否接收到了文件

[3.Spring MVC的方式](#3.Spring MVC的方式)

(1)目录

(2)导入依赖

(3)编写index.jsp

(4)suc.jsp

(5)配置springmvc.xml,视图解析器和文件上传配置

(6)FileController

(7)web.xml

(8)上传提交文件,观察是否收到上传的文件

4.Multipart的方式

(1)目录

(2)导入依赖

(3)index.jsp测试页面

(4)springmvc.xml

(5)FileUploadController

(6)web.xml

(7)上传并提交文件之后观察是否收到了文件

5.大文件拆分的方式

(1)目录

(2)导入依赖

(3)index.jsp

(4)ChunkUploadServlet

(5)MergeServlet


一、Java文件的三种上传方式

(一)本质区别

1.Servlet

(1)传统 Servlet

·依赖第三方jar包:commons-fileupload+commons-io/

·核心是手动解析请求流,通过ServletFileUpload把请求拆成FileItem,再遍历判断文件

·步骤多、代码繁琐、需要手动处理临时文件、内存阈值、文件名等细节

·用途:Servlet老项目写法,现在也用来理解底层

(2)原生Servlet3.0

·不用额外框架和jar包,serlet3.0自带支持

·用@MultipartConfig注解开启文件上传,通过requesat.getPart()直接获取文件

·代码简洁,不用手动解析请求流,服务器底层帮你处理了拆分、临时文件等逻辑

·用途:原生JavaWeb项目文件上传,比传统commons方式更现代、更简洁

2.传统SpringMVC

·基于SpringMVC+依赖 commons-fileupload第三方包

·强转MultipartHttpServletRequest拿文件

·配置麻烦、写法老旧

·用途:也算是淘汰了

3.MultipartFile上传

·Spring MVC/Spring Boot 官方封装好的标准写法

·直接在形参写MultipartFile接收,不同手动解析流

·配置简单、代码极简、支持单/多文件

·用途;现在所有企业、SpringMVCw/MavenWeb/SpringBoot统一用这个

(二)核心对比

|---------------|--------------------------|--------------|-------------|----------------|
| 方式 | 依赖 | 代码难度 | 配置量 | 实际开发使用 |
| 原生 Servlet | 仅 Servlet | 难、代码多 | 少 | 不用 |
| 传统 SpringMVC | 必须 commons-fileupload | 中等 | 多(需配解析器) | 淘汰 |
| MultipartFile | SpringMVC 核心 + commons 包 | 简单、极简 | 少 | 主流必用 |

(三)具体代码实现

1.传统Servlet

(1)目录
(2)pom.xml导入依赖
XML 复制代码
 <properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <maven.compiler.source>8</maven.compiler.source>
  <maven.compiler.target>8</maven.compiler.target>
  <!-- 统一版本号 -->
  <commons-io.version>2.11.0</commons-io.version>
  <commons-fileupload.version>1.4</commons-fileupload.version>
  <commons-codec.version>1.15</commons-codec.version>
</properties>

<!-- ✅ 正确的 Servlet 2.5 依赖(必须加 provided)-->
  <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>2.5</version>
    <scope>provided</scope>
  </dependency>

  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.1</version>
    <scope>test</scope>
  </dependency>

  <!-- 文件上传核心依赖 -->
  <dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>${commons-io.version}</version>
  </dependency>
  <dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>${commons-fileupload.version}</version>
  </dependency>

  <dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>${commons-codec.version}</version>
  </dependency>
</dependencies>
(3)编写测试页面index.jsp
html 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<body>
<h2><%= "Hello World!" %></h2>
<!-- 关键:必须加 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)编写FileServlet
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 {
            DiskFileItemFactory factory = new DiskFileItemFactory();
            ServletFileUpload upload = new ServletFileUpload(factory);

            List<FileItem> fileItems = upload.parseRequest(req);
            for (FileItem item : fileItems) {
                // 文件字段
                if (!item.isFormField() && "file".equals(item.getFieldName())) {
                    String originalFilename = item.getName();
                    // 截取后缀
                    String ext = originalFilename.substring(originalFilename.lastIndexOf("."));
                    String newFileName = UUID.randomUUID() + ext;
                    // 上传目录
                    File uploadDir = new File("D:/upload");
                    if (!uploadDir.exists()) {
                        uploadDir.mkdirs();
                    }
                    File saveFile = new File(uploadDir, newFileName);
                    item.write(saveFile);
                    System.out.println("上传成功:" + saveFile.getAbsolutePath());
                    resp.getWriter().write("文件上传成功!");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            resp.getWriter().write("上传失败:" + e.getMessage());
        }
    }
}

客户端以 post 方式 + enctype="multipart/form-data"** 提交文件表单

↓

请求进入 Servlet 后先设置编码

↓

通过ServletFileUpload判断是否为文件上传类型

↓

用磁盘工厂创建上传解析器,把请求解析成FileItem集合

↓

遍历判断出是文件类型且表单项 name 匹配

↓

截取文件后缀并用 UUID 生成新文件名,自动创建本地存放目录

↓

最后将文件写入磁盘保存,并给前端返回上传结果。

(5)编写web.xml
XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<web-app
        version="4.0"
        xmlns="http://xmlns.jcp.org/xml/ns/javaee"
        xmlns:javaee="http://xmlns.jcp.org/xml/ns/javaee"
        xmlns:xml="http://www.w3.org/XML/1998/namespace"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd">
  <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>
</web-app>
(6)配置tomcat后运行,上传文件
(7)点击提交后,查看本地文件夹是否有文件上传

2.Spring MVC的方式

(1)编写pom.xml,导入依赖
XML 复制代码
  <properties>
  <!-- 统一版本号 -->
  <commons-io.version>2.11.0</commons-io.version>
  <commons-fileupload.version>1.4</commons-fileupload.version>
  <commons-codec.version>1.15</commons-codec.version>
</properties>
<dependencies>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
  <version>5.0.2.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>5.0.2.RELEASE</version>
</dependency>
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>servlet-api</artifactId>
  <version>2.5</version>
</dependency>
<dependency>
  <groupId>commons-io</groupId>
  <artifactId>commons-io</artifactId>
  <version>${commons-io.version}</version>
</dependency>
<dependency>
  <groupId>commons-fileupload</groupId>
  <artifactId>commons-fileupload</artifactId>
  <version>${commons-fileupload.version}</version>
</dependency>
<dependency>
  <groupId>commons-codec</groupId>
  <artifactId>commons-codec</artifactId>
  <version>${commons-codec.version}</version>
</dependency>
<dependency>
  <groupId>commons-lang</groupId>
  <artifactId>commons-lang</artifactId>
  <version>2.6</version>
</dependency>
<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.13.1</version>
  <scope>test</scope>
</dependency>
</dependencies>

2.原生Servlet3.0

(1)目录
(2)导入依赖,因为 是原生tomcat自带的,所以只需导入这个tomcat的包
XML 复制代码
<dependency>
  <groupId>org.apache.tomcat.embed</groupId>
  <artifactId>tomcat-embed-core</artifactId>
  <version>9.0.75</version>
</dependency>
(3)编写测试页面index.jsp
html 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>文件上传测试</title>
</head>
<body>
<h3>单文件上传</h3>
<form action="upload30" method="post" enctype="multipart/form-data">
    <input type="file" name="file"/>
    <button type="submit">上传</button>
</form>
</body>
</html>
(4)配置好tomcat,打开页面上传并提交文件,观察本地是否接收到了文件

3.Spring MVC的方式

(1)目录
(2)导入依赖
XML 复制代码
  <properties>
  <!-- 统一版本号 -->
  <commons-io.version>2.11.0</commons-io.version>
  <commons-fileupload.version>1.4</commons-fileupload.version>
  <commons-codec.version>1.15</commons-codec.version>
</properties>
<dependencies>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
  <version>5.0.2.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>5.0.2.RELEASE</version>
</dependency>
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>servlet-api</artifactId>
  <version>2.5</version>
</dependency>
<dependency>
  <groupId>commons-io</groupId>
  <artifactId>commons-io</artifactId>
  <version>${commons-io.version}</version>
</dependency>
<dependency>
  <groupId>commons-fileupload</groupId>
  <artifactId>commons-fileupload</artifactId>
  <version>${commons-fileupload.version}</version>
</dependency>

<dependency>
  <groupId>commons-codec</groupId>
  <artifactId>commons-codec</artifactId>
  <version>${commons-codec.version}</version>
</dependency>
<dependency>
  <groupId>commons-lang</groupId>
  <artifactId>commons-lang</artifactId>
  <version>2.6</version>
</dependency>
<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.13.1</version>
  <scope>test</scope>
</dependency>
</dependencies>
(3)编写index.jsp
html 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<body>
<h2><%= "Hello World!" %></h2>
<!-- 关键:必须加 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)suc.jsp
html 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>上传文件入门案例MVC</title>
</head>
<body>
    <h1>文件上传成功!</h1>
</body>
</html>
(5)配置springmvc.xml,视图解析器和文件上传配置
XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="com.qcby"></context:component-scan>
    <!--配置视图解析器-->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/pages/"/>
        <property name="suffix"  value=".jsp"/>
    </bean>
    <!--后台mvc关于文件上传的配置-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxInMemorySize" value="10240000"/>
    </bean>
</beans>
(6)FileController
java 复制代码
package com.qcby.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.UUID;
@Controller
public class FileController {
    @RequestMapping("/updateFile")
//    public String uploadFile(MultipartFile file) throws IOException {
    public String uploadFile(HttpServletRequest request, HttpServletResponse response) throws IOException {
        System.out.println("成功");
        //转换request
        MultipartHttpServletRequest mr=(MultipartHttpServletRequest) request;
        //获得存储文件的map
        Map<String, MultipartFile> fileMap = mr.getFileMap();
        //获得文件
        MultipartFile pic = fileMap.get("file");
        // 创建该文件夹
        File file1 = new File("D:/Aupload01");
        // 判断该文件夹是否存在
        if(!file1.exists()){
            // 创建文件夹
            file1.mkdirs();
        }
        // 获取到上传文件的名称
        String filename = pic.getOriginalFilename();
        // 把文件的名称修改成为一的值 sdfs-csdf-fwer-sdfw
        String uuid = UUID.randomUUID().toString().replace("-", "").toUpperCase();
        // 唯一的值
        filename = uuid+"_"+filename;
        System.out.println("文件名称:"+filename);
        // 上传文件
        pic.transferTo(new File(file1,filename));
        return "suc";
    }
}
(7)web.xml
XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<web-app
        version="4.0"
        xmlns="http://xmlns.jcp.org/xml/ns/javaee"
        xmlns:javaee="http://xmlns.jcp.org/xml/ns/javaee"
        xmlns:xml="http://www.w3.org/XML/1998/namespace"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd">
  <display-name>Archetype Created Web Application</display-name>
  <filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!--配置加载springmvc配置文件加载-->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <!--启动加载-->
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>
(8)上传提交文件,观察是否收到上传的文件

4.Multipart的方式

(1)目录
(2)导入依赖
XML 复制代码
<properties>
  <maven.compiler.source>1.8</maven.compiler.source>
  <maven.compiler.target>1.8</maven.compiler.target>
  <spring.version>5.3.20</spring.version>
</properties>

<dependencies>
  <!-- Servlet API -->
  <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
    <scope>provided</scope>
  </dependency>

  <!-- Spring MVC -->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>${spring.version}</version>
  </dependency>

  <!-- 文件上传核心依赖(Spring MVC Multipart 必须) -->
  <dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
  </dependency>
  <dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.11.0</version>
  </dependency>
</dependencies>
(3)index.jsp测试页面
html 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>文件上传测试</title>
</head>
<body>
<h3>单文件上传</h3>
<form action="/file/upload" method="post" enctype="multipart/form-data">
    <input type="file" name="file"/><br>
    <button type="submit">上传</button>
</form>
</body>
</html>
(4)springmvc.xml
XML 复制代码
<!-- 开启注解扫描 -->
<context:component-scan base-package="com.qcby"/>
<!-- 开启Spring MVC注解驱动 -->
<mvc:annotation-driven/>
<!-- 文件上传解析器:MultipartFile 必须依赖这个 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!-- 编码 -->
    <property name="defaultEncoding" value="UTF-8"/>
    <!-- 单个文件最大 10MB -->
    <property name="maxUploadSize" value="10485760"/>
    <!-- 总请求最大 50MB -->
    <property name="maxUploadSizePerFile" value="10485760"/>
</bean>
(5)FileUploadController
java 复制代码
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.UUID;

@Controller
@RequestMapping("/file")
public class FileUploadController {

    @PostMapping("/upload")
    @ResponseBody
    public String upload(@RequestParam("file") MultipartFile file, HttpServletRequest request) {
        // 1. 判断文件是否为空
        if (file.isEmpty()) {
            return "上传失败:文件为空";
        }

        // 2. 获取上传目录(项目外的路径,避免部署后丢失)
        String uploadPath = "D:/MultipartUpload/";
        File dir = new File(uploadPath);
        if (!dir.exists()) {
            dir.mkdirs();
        }

        // 3. 生成唯一文件名,防止重名覆盖
        String originalName = file.getOriginalFilename();
        String suffix = originalName.substring(originalName.lastIndexOf("."));
        String newFileName = UUID.randomUUID().toString() + suffix;

        // 4. 保存文件
        try {
            file.transferTo(new File(uploadPath + newFileName));
            return "success!:" + uploadPath + newFileName;
        } catch (IOException e) {
            e.printStackTrace();
            return "上传失败:" + e.getMessage();
        }
    }

}
(6)web.xml
XML 复制代码
<!-- Spring MVC 前端控制器 -->
<servlet>
  <servlet-name>springmvc</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:springmvc.xml</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>springmvc</servlet-name>
  <url-pattern>/</url-pattern>
</servlet-mapping>

<!-- 字符编码过滤器 -->
<filter>
  <filter-name>encodingFilter</filter-name>
  <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
  <init-param>
    <param-name>encoding</param-name>
    <param-value>UTF-8</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>encodingFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>
(7)上传并提交文件之后观察是否收到了文件

5.大文件拆分的方式

(1)目录
(2)导入依赖
XML 复制代码
<properties>
  <maven.compiler.source>1.8</maven.compiler.source>
  <maven.compiler.target>1.8</maven.compiler.target>
</properties>

<dependencies>
  <!-- Servlet API -->
  <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
    <scope>provided</scope>
  </dependency>

  <!-- 文件上传依赖 -->
  <dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
  </dependency>
  <dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.11.0</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.3.33</version>
  </dependency>
</dependencies>
(3)index.jsp
html 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>大文件分片上传</title>
</head>
<body>
<h2>大文件分片上传(纯 Servlet 版)</h2>
<input type="file" id="fileInput"/>
<button onclick="upload()">开始上传</button>
<div id="progress"></div>

<script>
    const CHUNK_SIZE = 5 * 1024 * 1024; // 5MB

    async function upload() {
        const file = document.getElementById('fileInput').files[0];
        if (!file) {
            alert('请选择文件');
            return;
        }

        const fileName = file.name;
        const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
        let uploaded = 0;

        // 上传分片
        for (let i = 0; i < totalChunks; i++) {
            const start = i * CHUNK_SIZE;
            const end = Math.min(start + CHUNK_SIZE, file.size);
            const chunk = file.slice(start, end);

            let formData = new FormData();
            formData.append("file", chunk);
            formData.append("fileName", fileName);
            formData.append("chunkIndex", i);

            try {
                let res = await fetch("/uploadChunk", {
                    method: "POST",
                    body: formData
                });
                let text = await res.text();
                console.log(text);
            } catch (e) {
                console.log("上传失败", e);
                alert("上传分片失败");
                return;
            }

            uploaded++;
            document.getElementById("progress").innerText = "已上传:" + uploaded + "/" + totalChunks;
        }

        alert("所有分片上传完毕,开始合并......");

        // ↓↓↓ 这里是关键!必须走合并!
        try {
            let res = await fetch("/merge?fileName=" + encodeURIComponent(fileName), {
                method: "POST"
            });
            let text = await res.text();
            alert("合并结果:" + text);
        } catch (e) {
            console.log("合并失败", e);
            alert("合并失败");
        }
    }
</script>
</body>
</html>
(4)ChunkUploadServlet
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.annotation.WebServlet;
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;

@WebServlet("/uploadChunk")
public class ChunkUploadServlet extends HttpServlet {
    private static final String TEMP_DIR = "D:/upload/temp/";

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

        if (!ServletFileUpload.isMultipartContent(request)) {
            response.getWriter().write("error: 不是文件上传请求");
            return;
        }

        try {
            DiskFileItemFactory factory = new DiskFileItemFactory();
            ServletFileUpload upload = new ServletFileUpload(factory);
            List<FileItem> items = upload.parseRequest(request);

            String fileName = null;
            int chunkIndex = 0;
            // 修正:类型改为 FileItem
            FileItem chunkFile = null;

            for (FileItem item : items) {
                if (item.isFormField()) {
                    String fieldName = item.getFieldName();
                    if ("fileName".equals(fieldName)) {
                        fileName = item.getString("UTF-8");
                    } else if ("chunkIndex".equals(fieldName)) {
                        chunkIndex = Integer.parseInt(item.getString("UTF-8"));
                    }
                } else {
                    // 这里赋值就不会报红了
                    chunkFile = item;
                }
            }

            if (fileName == null || chunkFile == null) {
                response.getWriter().write("error: 参数不全");
                return;
            }

            // 2. 为每个文件创建临时目录
            File tempDir = new File(TEMP_DIR + fileName + "/");
            if (!tempDir.exists()) {
                tempDir.mkdirs();
            }

            // 3. 保存分片
            File chunk = new File(tempDir, chunkIndex + ".part");
            chunkFile.write(chunk);

            response.getWriter().write("success: 分片" + chunkIndex + "上传完成");

        } catch (Exception e) {
            e.printStackTrace();
            response.getWriter().write("error: " + e.getMessage());
        }
    }
}
(5)MergeServlet
java 复制代码
package com.qcby.servlet;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.Arrays;

@WebServlet("/merge")
public class MergeServlet extends HttpServlet {
    private static final String TEMP_DIR = "D:/upload/temp/";
    private static final String FINAL_DIR = "D:/upload/final/";

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

        String fileName = request.getParameter("fileName");
        if (fileName == null || fileName.isEmpty()) {
            response.getWriter().write("文件名不能为空");
            return;
        }

        File tempDir = new File(TEMP_DIR + fileName + "/");
        if (!tempDir.exists()) {
            response.getWriter().write("分片不存在");
            return;
        }

        File[] chunks = tempDir.listFiles();
        if (chunks == null || chunks.length == 0) {
            response.getWriter().write("没有分片");
            return;
        }

        // 排序
        Arrays.sort(chunks, (a, b) -> {
            int i1 = Integer.parseInt(a.getName().replace(".part", ""));
            int i2 = Integer.parseInt(b.getName().replace(".part", ""));
            return Integer.compare(i1, i2);
        });

        // 自动创建 final 文件夹
        File finalDir = new File(FINAL_DIR);
        if (!finalDir.exists()) {
            finalDir.mkdirs();
        }

        File finalFile = new File(FINAL_DIR + fileName);

        // 合并
        try (FileOutputStream fos = new FileOutputStream(finalFile);
             BufferedOutputStream bos = new BufferedOutputStream(fos)) {

            byte[] buffer = new byte[1024 * 1024];
            for (File chunk : chunks) {
                try (FileInputStream fis = new FileInputStream(chunk);
                     BufferedInputStream bis = new BufferedInputStream(fis)) {
                    int len;
                    while ((len = bis.read(buffer)) != -1) {
                        bos.write(buffer, 0, len);
                    }
                }
                chunk.delete();
            }
        }

        tempDir.delete();
        response.getWriter().write("合并成功!文件路径:" + finalFile.getAbsolutePath());
    }
}
相关推荐
智能直播1 小时前
网络RTMP拉流不卡顿、声音不同步,一文讲透缓冲区、时间戳
开发语言·ffmpeg·php·音视频·视频编解码
欣栀♡1 小时前
JVM 频繁 Full GC 先看日志括号里的 cause,jstat 的 FGC 在 JDK 8 上会骗人
java·jvm
IT研究室1 小时前
最新计算机毕业设计选题推荐-基于spring boot的葡萄酒文化宣传网站-网站-文档指导-Java-springboot
java·spring boot·课程设计
慧都小项2 小时前
MyEclipse 中创建和测试 JAX-RS REST 接口:从项目配置到 REST Explorer
java·restful·myeclipse·jax-rs
Wang's Blog2 小时前
Java 项目实战: 交友项目 - 入门案例:编写 Dubbo 服务提供者
java·dubbo·交友
墨云安全2 小时前
内网资产无法暴露到公网,怎么做安全检测?2026 年内网渗透测试的落地路径
开发语言·安全·网络安全·渗透测试·php
秋饼2 小时前
Spring AI 2.0 结构化输出企业级实战:从 BeanOutputConverter 源码到生产级解析容错
java·ai·技术分享·后端开发
摇滚侠2 小时前
《计算机是如何工作的:人人都能懂的计算机软硬件工作原理》第 9 章 机器码与汇编语言 阅读笔记 9
java·开发语言·笔记
Cc.Y2 小时前
Java零基础入门:深入Java核心:文件IO与序列化全解
java·开发语言·python