JAVA技术-大文件上传的优雅设计

#VibeCoding·九月创作之星挑战赛#

背景场景

在实际开发中,我们经常遇到大文件上传的需求,比如:

  • • 用户上传 2GB 的视频;

  • • 后台上传大文件 ZIP 数据包;

  • • 文件上传过程中网络中断。

如果我们仍用传统的 MultipartFile 一次性上传,很快会遇到以下问题:

  • • 前端体验差: 上传失败要重传;

  • • 后端压力大: 占用大量内存;

  • • 不具备扩展性: 难以支持分布式部署。

目标设计

设计一个支持以下特性的文件上传接口:

  • • 分片上传: 大文件拆成小块上传;

  • • 断点续传: 网络中断后从断点继续;

  • • 高可用性: 支持分布式部署,不依赖本地文件;

  • • 可扩展性: 支持对象存储如 MinIO、OSS;

  • • CDN 加速: 支持文件分发、下载优化。

技术选型

图片

模块设计图

图片

 

核心表结构设计

CREATE TABLE file_upload_record (
    id BIGINTPRIMARY KEY AUTO_INCREMENT,
    file_md5 VARCHAR(64) NOT NULL,
    file_name VARCHAR(255),
    total_chunks INT,
    uploaded_chunks INTDEFAULT0,
    is_complete BOOLEANDEFAULTFALSE,
    created_at TIMESTAMPDEFAULTCURRENT_TIMESTAMP
);

核心接口设计

检查文件是否已上传
@GetMapping("/upload/check")
public ResponseEntity<?> checkFile(@RequestParam String fileMd5) {
    FileUploadRecordrecord= recordRepository.findByFileMd5(fileMd5);
    if (record != null && record.getIsComplete()) {
        return ResponseEntity.ok(Map.of("uploaded", true, "url", getFileUrl(fileMd5)));
    }
    return ResponseEntity.ok(Map.of("uploaded", false));
}
上传分片接口
@PostMapping("/upload/chunk")
public ResponseEntity<?> uploadChunk(
    @RequestParam String fileMd5,
    @RequestParamint chunkIndex,
    @RequestParam MultipartFile filePart
) throws IOException {
    // 临时保存对象名:如 fileMd5/chunkIndex
    StringobjectName= String.format("upload/%s/%d.part", fileMd5, chunkIndex);
    
    // 上传到 MinIO
    minioClient.putObject(
        PutObjectArgs.builder()
            .bucket(bucketName)
            .object(objectName)
            .stream(filePart.getInputStream(), filePart.getSize(), -1)
            .contentType(filePart.getContentType())
            .build()
    );

    // 更新数据库状态
    recordService.markChunkUploaded(fileMd5, chunkIndex);
    return ResponseEntity.ok("Chunk uploaded");
}
合并分片并生成最终文件
@PostMapping("/upload/merge")
public ResponseEntity<?> mergeChunks(@RequestParam String fileMd5, @RequestParamint totalChunks) throws Exception {
    StringfinalObjectName="upload/" + fileMd5 + ".final";

    // 合并文件(使用 MinIO 的 composeObject)
    List<ComposeSource> sources = newArrayList<>();
    for (inti=0; i < totalChunks; i++) {
        sources.add(
            ComposeSource.builder()
                .bucket(bucketName)
                .object(String.format("upload/%s/%d.part", fileMd5, i))
                .build()
        );
    }

    minioClient.composeObject(
        ComposeObjectArgs.builder()
            .bucket(bucketName)
            .object(finalObjectName)
            .sources(sources)
            .build()
    );

    // 标记上传完成
    recordService.markComplete(fileMd5);

    return ResponseEntity.ok(Map.of("url", getFileUrl(fileMd5)));
}

核心逻辑详解

为什么要用 fileMd5 作为分片唯一标识?

  • • MD5 可标志文件唯一性,便于去重和断点续传;

  • • 客户端可预先计算 MD5,与服务器确认哪些分片已上传。

MinIO 是如何支持分片合并的?

  • • MinIO 支持 composeObject,可将多个对象合并为一个;

  • • 类似于 AWS S3 的“多段上传”功能;

  • • 合并操作在服务端进行,避免网络传输压力。

如果网络中断怎么办?

  • • 客户端每上传一个分片记录状态;

  • • 重新发起上传前调用/check查询已上传分片;

  • • 只上传缺失部分,节省时间和带宽。

可扩展性设计

模块化存储接口

publicinterfaceFileStorageService {
    voiduploadChunk(String objectName, InputStream stream, long size);
    voidmergeChunks(String finalName, List<String> chunkNames);
    String getFileUrl(String objectName);
}
  • • 实现类可对接 OSS、MinIO、FastDFS 等;

  • • 降低耦合,支持替换存储供应商。

性能与高可用优化

图片

返回 CDN 地址

public String getFileUrl(String fileMd5) {
    return cdnDomain + "/upload/" + fileMd5 + ".final";
}
  • • 文件合并后部署到 CDN;

  • • 前端访问地址统一为 CDN 加速域名。

总结

我们通过以下方案构建了一个高可用、可扩展的大文件上传接口:

  • • 支持 断点续传、分片上传;

  • • 使用 MinIO/S3 对象存储;

  • • 支持 CDN 分发;

  • • 可轻松扩展存储后端或接入云服务。

推荐工具 & 框架

  • • MinIO Java SDK

  • • Spring Boot + Spring Web

  • • Redis(限流、幂等)

  • • MySQL(记录上传状态)

结语

这套文件上传方案,特别适合对稳定性高并发用户体验要求较高的系统。希望本文能为你在实际项目中实现大文件上传提供思路。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

汤姆yu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值