Java实现大文件上传与断点续传的全过程

 更新时间:2026年01月21日 09:12:02   作者:Geek攻城猫  
本文介绍了如何使用Java(SpringBoot)实现高效、可靠的大型文件上传系统,包括分片上传、断点续传等核心设计思想和后端实现步骤,通过优化建议和进阶方案,进一步提升了用户体验和系统健壮性,需要的朋友可以参考下

在现代 Web 应用中,用户经常需要上传大型文件——如高清视频、设计图纸或数据库备份。然而,受限于网络稳定性、服务器内存和浏览器限制,直接上传大文件极易失败。为此,“分片上传 + 断点续传”成为业界标准解决方案。本文将深入讲解如何使用 Java(基于 Spring Boot) 实现高效、可靠的大文件上传系统。

一、为什么需要断点续传?

  • 网络不稳定:上传过程中断后,无需从头开始。
  • 节省带宽与时间:仅重传失败或未传的分片。
  • 提升用户体验:支持上传进度显示、暂停/恢复。
  • 避免服务器压力:避免一次性加载整个大文件到内存。

二、核心设计思想

1. 文件分片(Chunking)

将一个大文件按固定大小(如 2MB)切分为多个小块(chunks),每个块独立上传。

2. 唯一标识

使用文件内容的 MD5 值 作为唯一 ID,既可识别文件,又能避免重复上传相同内容。

3. 分片管理

  • 后端为每个文件创建临时目录,按序号存储分片。
  • 提供接口查询“已上传分片列表”,实现断点检测。

4. 合并与清理

所有分片收齐后,按顺序合并成完整文件,并清理临时数据。

三、后端实现(Spring Boot)

1. 项目依赖(Maven)

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.11.0</version>
    </dependency>
</dependencies>

2. 控制器代码

@RestController
@RequestMapping("/api/upload")
public class ResumableUploadController {

    private static final String TEMP_DIR = "uploads/temp/";
    private static final String FINAL_DIR = "uploads/final/";

    /**
     * 检查哪些分片已上传(用于断点续传)
     */
    @GetMapping("/check")
    public Set<Integer> checkUploadedChunks(@RequestParam String fileMd5) {
        Set<Integer> uploaded = new HashSet<>();
        Path tempPath = Paths.get(TEMP_DIR, fileMd5);
        if (Files.exists(tempPath)) {
            try (Stream<Path> stream = Files.list(tempPath)) {
                stream.forEach(p -> {
                    String name = p.getFileName().toString();
                    if (name.matches("\\d+")) {
                        uploaded.add(Integer.parseInt(name));
                    }
                });
            } catch (IOException e) {
                // 日志记录
            }
        }
        return uploaded;
    }

    /**
     * 上传单个分片
     */
    @PostMapping("/chunk")
    public ResponseEntity<?> uploadChunk(
            @RequestParam String fileMd5,
            @RequestParam int chunkIndex,
            @RequestParam MultipartFile chunk) {

        try {
            Path tempDir = Paths.get(TEMP_DIR, fileMd5);
            Files.createDirectories(tempDir);
            Path chunkFile = tempDir.resolve(String.valueOf(chunkIndex));
            chunk.transferTo(chunkFile.toFile());
            return ResponseEntity.ok().build();
        } catch (IOException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }

    /**
     * 合并所有分片为完整文件
     */
    @PostMapping("/merge")
    public ResponseEntity<?> mergeChunks(
            @RequestParam String fileMd5,
            @RequestParam String fileName,
            @RequestParam int totalChunks) {

        try {
            Path finalPath = Paths.get(FINAL_DIR, fileName);
            Files.createDirectories(finalPath.getParent());

            try (OutputStream out = Files.newOutputStream(finalPath)) {
                for (int i = 0; i < totalChunks; i++) {
                    Path chunk = Paths.get(TEMP_DIR, fileMd5, String.valueOf(i));
                    if (!Files.exists(chunk)) {
                        return ResponseEntity.badRequest()
                                .body("Missing chunk: " + i);
                    }
                    byte[] data = Files.readAllBytes(chunk);
                    out.write(data);
                }
            }

            // 清理临时分片
            FileUtils.deleteDirectory(new File(TEMP_DIR + fileMd5));

            return ResponseEntity.ok(Map.of("success", true, "path", finalPath.toString()));
        } catch (IOException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }
}

注意:实际项目中应加入文件名校验、权限控制、异常日志等。

四、前端配合逻辑(简要)

前端需完成以下步骤:

  1. 计算文件 MD5(推荐使用 spark-md5 库)。
  2. 调用 /check 接口 获取已上传分片索引。
  3. 循环上传未完成的分片,每个请求携带:
    • fileMd5
    • chunkIndex
    • 分片 Blob 数据
  4. 全部上传完成后,调用 /merge 触发合并
// 示例:上传一个分片
const uploadChunk = async (fileMd5, index, blob) => {
  const formData = new FormData();
  formData.append('fileMd5', fileMd5);
  formData.append('chunkIndex', index);
  formData.append('chunk', blob);
  await fetch('/api/upload/chunk', { method: 'POST', body: formData });
};

五、关键优化建议

优化点说明
MD5 计算前端计算可避免重复上传;若安全要求高,后端也可二次校验。
并发上传允许同时上传多个分片(如 3~5 个),提升速度。
超时清理定时任务删除 24 小时未合并的临时分片,防止磁盘占满。
限流与鉴权防止恶意上传,限制单用户上传频率和总大小。
进度反馈前端根据已传分片数实时更新进度条。

六、进阶方案

1. 使用 TUS 协议

TUS 是一个开源的 Resumable Upload 标准。Java 社区有 tus-java-server 实现,开箱即用,支持跨平台、跨语言。

2. 对接云存储

阿里云 OSS、腾讯云 COS、AWS S3 等均提供 分片上传 API。可将分片直接上传至云存储,后端仅负责协调,大幅降低服务器负载。

七、总结

通过“分片上传 + 断点续传”,我们不仅能可靠地处理 GB 级文件,还能显著提升用户体验和系统健壮性。虽然实现细节较多,但核心逻辑清晰:切分 → 上传 → 校验 → 合并

在实际项目中,建议结合业务需求选择自研或集成成熟方案。对于高并发、高可靠场景,优先考虑云厂商的分片上传能力;对于内部系统或定制化需求,本文提供的 Java 实现可作为坚实基础。

以上就是Java实现大文件上传与断点续传的全过程的详细内容,更多关于Java大文件上传与断点续传的资料请关注脚本之家其它相关文章!

相关文章

  • Java编程中应用的GUI设计基础

    Java编程中应用的GUI设计基础

    这篇文章主要介绍了Java编程中应用的GUI设计基础,为一些Java开发CS类型应用的基础概念知识,需要的朋友可以参考下
    2015-10-10
  • IDEA .iml文件及.idea文件夹超详细讲解

    IDEA .iml文件及.idea文件夹超详细讲解

    iml文件是IntelliJ IDEA自己创建的模块文件,用于java应用开发,存储一些模块相关的信息,比如一个Java组建,插件组建,Maven组建等,这篇文章主要介绍了IDEA .iml文件及.idea文件夹,需要的朋友可以参考下
    2023-09-09
  • SpringBoot如何读取mock数据(高效调试接口)

    SpringBoot如何读取mock数据(高效调试接口)

    本文介绍如何在SpringBoot项目中读取resources目录下的mock数据文件,以便高效调试接口,在 Spring Boot 项目中,通常会将静态资源或配置文件放在 src/main/resources 目录下,下面通过实例给大家详细介绍,感兴趣的朋友一起看看吧
    2024-12-12
  • 通过JWT来解决登录认证问题的方案

    通过JWT来解决登录认证问题的方案

    Json web token (JWT),是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC7519),该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景,本文给大家介绍了如何通过 JWT 来解决登录认证问题,需要的朋友可以参考下
    2024-12-12
  • java创建xls文件放到指定文件夹中实现方式

    java创建xls文件放到指定文件夹中实现方式

    本文介绍了如何在Java中使用ApachePOI库创建和操作Excel文件,重点是如何创建一个XLS文件并将其放置到指定文件夹中
    2025-11-11
  • springboot集成RocketMQ过程及使用示例详解

    springboot集成RocketMQ过程及使用示例详解

    这篇文章主要为大家介绍了springboot集成RocketMQ过程及使用示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-07-07
  • java中CopyOnWriteArrayList源码解析

    java中CopyOnWriteArrayList源码解析

    为了将读取的性能发挥到极致,jdk中提供了CopyOnWriteArrayList类,下面这篇文章主要给大家介绍了关于java中CopyOnWriteArrayList源码解析的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-02-02
  • java_时间戳与Date_相互转化的实现代码

    java_时间戳与Date_相互转化的实现代码

    本篇文章是对java_时间戳与Date_相互转化的实现代码进行了详细的分析介绍,需要的朋友参考下如下。
    2016-11-11
  • java 中InputStream,String,File之间的相互转化对比

    java 中InputStream,String,File之间的相互转化对比

    这篇文章主要介绍了java 中InputStream,String,File之间的相互转化对比的相关资料,需要的朋友可以参考下
    2017-04-04
  • java解析背包问题实例代码(简单易懂)

    java解析背包问题实例代码(简单易懂)

    背包问题是计算机科学中的经典问题,涉及到在给定的一组物品中选择最佳的物品组合,以使其总重量不超过背包所能承载的限制,同时获得最大的总价值,这篇文章主要介绍了java解析背包问题的相关资料,需要的朋友可以参考下
    2025-10-10

最新评论