找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

193

积分

0

好友

19

主题
发表于 5 小时前 | 查看: 1| 回复: 0

在正式讲解之前,先通过一个实际演示了解大文件上传的完整流程。下面演示的是上传1GB压缩包的过程,支持分片上传、断点续传、暂停和恢复功能。

图片

在常规SaaS系统中,文件上传功能通常设计得较为简单:前端进行分片上传,后端合并分片后存储到OSS或服务器路径,最后返回URL地址。这种方案在大多数场景下确实适用,但在私有化部署项目中却面临全新挑战。

业务场景特殊性

近期在政企内部系统项目中,需要处理AI大模型客服问答功能的文档上传需求。客户需将内部文档、手册、规范等资料打包上传至服务器,用于后续的向量化、知识检索和模型训练。这类场景具有以下特征:

  1. 文件数量多:动辄数百上千份(Word、PDF、PPT、Markdown等格式)
  2. 文件体积大:压缩包通常达到几GB甚至几十GB
  3. 上传环境复杂:内网或局域网环境,部分完全断网
  4. 安全要求高:文件涉及保密资料,不能经过云端OSS
  5. 审计需求:需记录上传人、时间、存储位置等完整信息
  6. 后续处理:需要自动解压、文本解析、拆页、向量化后存入Milvus或pgvector

若继续采用"SaaS简单上传+云存储"方案,将面临诸多问题:

  • 上传中断后刷新页面需重传整个文件包
  • 集群部署时分片分布在不同机器无法合并
  • 多人同时上传可能发生文件覆盖或路径冲突
  • 缺乏上传记录和用户追踪机制
  • 无法满足政企审计、合规和保密要求

常规项目中的上传方案

回顾常规Web项目,特别是SaaS系统和后台管理系统,后端通常只提供单个/upload接口。前端获取文件后直接调用该接口,后端保存文件并返回URL即完成流程。

在某些项目中,前端甚至不将文件传递给业务服务,而是通过云存储SDK(如阿里云OSS、腾讯云COS、七牛云等)直接上传到云端,获取文件地址后再回传给后端保存。

这种模式在SaaS系统或轻量级业务中非常普遍,主要基于以下考量:

  1. 文件体积小:多为几MB的图片、PDF或Excel文件
  2. 云存储稳定:具备完整的上传、下载、访问SDK支持
  3. 公网部署环境:无需考虑局域网或内网断网问题
  4. 安全要求低:文件内容不涉及涉密数据
  5. 用户体验优先:直传云端是最便捷的方案

这种"单upload接口"或"前端直传OSS"模式面向的是通用型SaaS场景,在绝大多数互联网业务中既高效又便捷。但一旦切换到政企、私有化部署或AI训练平台环境,问题的核心就转变为文件上传后的可控性、可追溯性和安全性。

前端大文件上传技术方案

在重新设计后端接口前,先梳理前端常见的大文件上传方案。近年来前端技术在此领域已相当成熟,主流方案围绕几个核心点展开:秒传检测、分片上传、断点续传、并发控制和进度展示。

前端获取文件后,首先计算文件哈希值(如MD5),用于秒传检测:如果服务器已存在相同文件,可直接跳过上传环节,节省时间和带宽资源。

接下来是分片上传环节。面对大文件时,前端将文件拆分为固定大小的数据块(通常5MB或10MB),逐片上传。这种方式避免了单次传输大文件导致的浏览器卡顿或网络中断问题。

断点续传功能通过记录已上传分片状态实现。当上传过程因网络中断或页面刷新而中断时,下次只需继续上传未完成的分片,无需重新传输整个文件包。

在性能优化方面,前端实施并发控制策略。例如同时上传3-5个分片,完成一个立即补充下一个,整体速度显著优于单线程串行上传。

最后是进度展示功能。通过监听每个分片的上传状态,前端可实时计算整体进度,为用户提供上传百分比或进度条反馈,提升操作体验。

基于这种相对标准的前端逻辑,我们构建了更贴合企业私有化环境的上传接口控制体系,明确前后端职责划分:前端负责切片、控制与恢复;后端负责存储、校验与合并。

后端接口架构设计

为配合前端标准上传流程,将后端接口拆分为多个独立阶段:秒传检查、初始化任务、上传分片、合并文件、暂停任务、取消任务、任务列表。每个接口职责单一,便于后期扩展维护。

一、/upload/check —— 秒传检查

作为流程第一步,用于检测文件是否已存在。前端计算完文件全局MD5后调用此接口,若数据库中存在相同哈希文件,直接返回"已存在"状态,前端跳过上传环节。

请求示例:

POST /api/upload/check
{
  "fileHash": "md5_abc123def456",
  "fileName": "training-docs.zip",
  "fileSize": 5342245120
}

返回示例:

{
  "success": true,
  "data": {
    "exists": false
  }
}

exists = true时,表示服务端已存在该文件,直接执行秒传成功逻辑。

伪代码示例:

@PostMapping("/check")
public Result<?> checkFile(@RequestBody Map<String, Object> body) {
    // 1. 校验fileHash参数是否为空
    // 2. 查询file_info表是否已有该文件
    // 3. 如果文件已存在,直接返回秒传成功(exists = true)
    // 4. 如果文件不存在,查询upload_task表中是否有未完成任务
}
二、/upload/init —— 初始化上传任务

当文件不存在时,需初始化新上传任务。此接口创建upload_task记录,返回唯一uploadId,前端使用该标识符管理整个上传过程。

请求示例:

POST /api/upload/init
{
  "fileHash": "md5_abc123def456",
  "fileName": "training-docs.zip",
  "totalChunks": 320,
  "chunkSize": 5242880
}

返回示例:

{
  "success": true,
  "data": {
    "uploadId": "b4f8e3a7-1a0c-4a1d-88af-61e98d91a49b",
    "uploadedChunks": []
  }
}

uploadedChunks字段支持断点续传,返回已上传分片索引数组。

伪代码示例:

@PostMapping("/init")
public Result<UploadInitResponse> initUpload(@RequestBody UploadInitRequest request) {
    // 1. 检查是否已有同fileHash任务,若有则返回旧任务信息
    // 2. 创建新的upload_task记录,生成uploadId
    // 3. 初始化分片数量、大小、状态等信息
    // 4. 返回uploadId与已上传分片索引列表
}
三、/upload/chunk —— 上传单个分片

这是调用频率最高的接口。每个分片单独上传,服务端保存为临时文件并写入upload_chunk表。上传成功后更新upload_task进度信息。

请求示例(表单上传):

POST /api/upload/chunk
Content-Type: multipart/form-data

formData:
  uploadId: b4f8e3a7-1a0c-4a1d-88af-61e98d91a49b
  chunkIndex: 0
  chunkSize: 5242880
  chunkHash: md5_001
  file: (二进制分片数据)

返回示例:

{
  "success": true,
  "data": {
    "uploadId": "b4f8e3a7-1a0c-4a1d-88af-61e98d91a49b",
    "chunkIndex": 0,
    "chunkSize": 5242880
  }
}

伪代码示例:

@PostMapping(value = "/chunk", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public Result<?> uploadChunk(@ModelAttribute UploadChunkRequest req) {
    // 1. 校验任务状态,禁止上传已取消或已完成任务
    // 2. 检查本地目录(或云端存储桶)是否存在,不存在则创建
    // 3. 接收当前分片文件并写入临时路径
    // 4. 写入upload_chunk表,标记状态为"已上传"
    // 5. 更新upload_task的uploaded_chunks数量
}
四、/upload/merge —— 合并分片

当前端确认所有分片上传完成后调用此接口。后端检查分片完整性后按索引顺序合并,删除临时分片文件,更新upload_task状态为"完成"。若启用云存储,此步骤可直接将合并后文件上传至OSS。

请求示例:

POST /api/upload/merge
{
  "uploadId": "b4f8e3a7-1a0c-4a1d-88af-61e98d91a49b",
  "fileHash": "md5_abc123def456"
}

返回示例:

{
  "success": true,
  "message": "文件合并成功",
  "data": {
    "storagePath": "/data/uploads/training-docs.zip"
  }
}

伪代码示例:

@PostMapping("/merge")
public Result<?> mergeFile(@RequestBody UploadMergeRequest req) {
    // 1. 检查upload_task状态是否允许合并
    // 2. 校验所有分片是否都上传完成
    // 3. 如果是本地存储:按chunk_index顺序流式合并文件
    // 4. 如果是云存储:调用云端分片合并API(如OSS、COS)
    // 5. 校验文件hash完整性,更新任务状态为COMPLETED
    // 6. 将最终文件信息写入file_info表
}
五、/upload/pause —— 暂停任务

用于上传过程中手动暂停任务。前端在网络波动或用户主动操作时调用,后端更新任务状态为"已暂停",记录当前已上传分片数。

请求示例:

POST /api/upload/pause
{
  "uploadId": "b4f8e3a7-1a0c-4a1d-88af-61e98d91a49b"
}

返回示例:

{
  "success": true,
  "message": "任务已暂停"
}

伪代码示例:

@PostMapping("/pause")
public Result<Void> pauseUpload(@RequestBody UploadPauseRequest req) {
    // 1. 查找对应的upload_task
    // 2. 更新任务状态为"已暂停"
    // 3. 返回任务状态确认信息
}
六、/upload/cancel —— 取消任务

用户放弃上传时调用此接口。后端将任务状态标记为"已取消",清理对应临时分片文件,避免磁盘空间浪费。

请求示例:

POST /api/upload/cancel
{
  "uploadId": "b4f8e3a7-1a0c-4a1d-88af-61e98d91a49b"
}

返回示例:

{
  "success": true,
  "message": "任务已取消"
}

伪代码示例:

@PostMapping("/cancel")
public Result<?> cancelUpload(@RequestBody UploadCancelRequest req) {
    // 1. 查找对应的upload_task
    // 2. 更新任务状态为"已取消"
    // 3. 删除或标记已上传分片文件为待清理
    // 4. 返回操作结果
}
七、/upload/list —— 查询任务列表

用于管理后台查看上传任务整体情况,展示文件名、大小、进度、状态、上传人等信息,便于追踪和审计。

请求示例:

GET /api/upload/list

返回示例:

{
  "success": true,
  "data": [
    {
      "uploadId": "b4f8e3a7-1a0c-4a1d-88af-61e98d91a49b",
      "fileName": "training-docs.zip",
      "status": "COMPLETED",
      "uploadedChunks": 320,
      "totalChunks": 320,
      "uploader": "admin",
      "createdAt": "2025-10-20 14:30:12"
    }
  ]
}

伪代码示例:

@GetMapping("/list")
public Result<List<UploadTaskSummary>> listUploadTasks() {
    // 1. 查询所有上传任务
    // 2. 按创建时间或状态排序
    // 3. 返回任务摘要信息
}

接口调用时序

完整上传过程调用顺序如下:

1. /upload/check     → 秒传检测
2. /upload/init      → 初始化上传任务
3. /upload/chunk     → 循环上传所有分片
4. /upload/merge     → 所有分片完成后合并

可选控制接口:/upload/pause/upload/cancel用于任务控制,/upload/list用于任务追踪与审计。

接口调用示意图

下图展示前端、后端、数据库在整个上传过程中的交互关系:

Untitled diagram-2025-11-10-031845.png

这种设计具有以下优势:

  1. 逻辑衔接顺畅:接口职责明确,图示总结清晰
  2. 视觉节奏平衡:图文结合缓解阅读疲劳
  3. 承上启下:既总结接口流程,又自然引出数据库设计

这套接口设计全面覆盖企业级项目中的大文件上传需求。接下来探讨支撑这些接口的Java后端架构设计。

数据库架构设计

为实现稳定的上传服务,后端需要记录任务状态、分片信息和文件存储路径的数据库结构。上传操作通常持续数分钟至数小时,需确保任务状态可追踪、可恢复,并支持集群部署环境。

设计三张核心表:upload_task(上传任务表)、upload_chunk(分片表)、file_info(文件信息表),分别管理任务、分片和最终文件的数据关系。

一、upload_task —— 上传任务表

作为上传过程的"总账",每个文件上传任务在此生成记录,保存任务的全局信息,包括文件名、大小、上传进度、状态和存储方式等。

CREATE TABLE `upload_task` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `upload_id` varchar(64) NOT NULL COMMENT '任务唯一ID(UUID)',
  `file_hash` varchar(64) NOT NULL COMMENT '文件哈希(用于秒传与断点续传)',
  `file_name` varchar(255) NOT NULL COMMENT '文件名称',
  `file_size` bigint(20) NOT NULL COMMENT '文件总大小(字节)',
  `chunk_size` bigint(20) NOT NULL COMMENT '每个分片大小(字节)',
  `total_chunks` int(11) NOT NULL COMMENT '分片总数',
  `uploaded_chunks` int(11) DEFAULT '0' COMMENT '已上传分片数量',
  `status` tinyint(4) DEFAULT '0' COMMENT '任务状态:0-待上传 1-上传中 2-合并中 3-完成 4-取消 5-失败 6-已合并 7-已暂停',
  `storage_type` varchar(32) DEFAULT 'local' COMMENT '存储类型:local/oss/cos/minio/s3等',
  `storage_url` varchar(512) DEFAULT NULL COMMENT '文件最终存储地址(云端或本地路径)',
  `local_path` varchar(512) DEFAULT NULL COMMENT '本地临时文件或合并文件路径',
  `remark` varchar(255) DEFAULT NULL COMMENT '备注信息',
  `uploader` varchar(64) DEFAULT NULL COMMENT '上传人',
  `created_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `upload_id` (`upload_id`),
  KEY `idx_hash` (`file_hash`),
  KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='上传任务表(支持多种云存储)';

设计要点:

  • upload_id 前端初始化任务后由后端生成的唯一标识
  • file_hash 支持秒传逻辑
  • status 控制任务生命周期
  • storage_type、storage_url 兼容多种存储方案
  • uploaded_chunks 支持任务恢复和断点续传
二、upload_chunk —— 分片表

对应每个上传任务的所有分片,每个分片单独记录,用于追踪上传状态。支持断点续传、进度统计和合并前完整性检查。

CREATE TABLE `upload_chunk` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `upload_id` varchar(64) NOT NULL COMMENT '所属上传任务ID',
  `chunk_index` int(11) NOT NULL COMMENT '分片索引(从0开始)',
  `chunk_size` bigint(20) NOT NULL COMMENT '实际分片大小(字节)',
  `chunk_hash` varchar(64) DEFAULT NULL COMMENT '可选:分片hash(用于高级去重)',
  `status` tinyint(4) DEFAULT '0' COMMENT '状态:0-待上传 1-已上传 2-已合并',
  `local_path` varchar(512) DEFAULT NULL COMMENT '分片本地路径',
  `created_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uniq_task_chunk` (`upload_id`,`chunk_index`),
  KEY `idx_upload_id` (`upload_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='上传分片表';

设计要点:

  • upload_id 任务外键,与upload_task对应
  • chunk_index 分片顺序,合并时按此排序
  • chunk_hash 可选字段,用于完整性校验
  • status 控制上传进度
  • 唯一索引避免重复插入分片

通过此表实现断点续传:用户重新上传时,后端返回未完成分片索引,前端跳过已上传部分。

三、file_info —— 文件信息表

记录上传完成后的最终文件信息,作为系统文件索引表。文件合并成功并通过校验后,写入此表记录。支持秒传功能,供后续文档解析或向量化任务使用。

CREATE TABLE `file_info` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `file_hash` varchar(64) NOT NULL COMMENT '文件hash,用于秒传',
  `file_name` varchar(255) NOT NULL COMMENT '文件名称',
  `file_size` bigint(20) NOT NULL COMMENT '文件大小',
  `storage_type` varchar(32) DEFAULT 'local' COMMENT '存储类型:local/oss/cos/minio/s3等',
  `storage_url` varchar(512) DEFAULT NULL COMMENT '文件最终存储地址(云端或本地路径)',
  `uploader` varchar(64) DEFAULT NULL COMMENT '上传人',
  `status` tinyint(4) DEFAULT '1' COMMENT '状态:1-正常,2-删除中,3-已归档',
  `remark` varchar(255) DEFAULT NULL COMMENT '备注',
  `created_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `file_hash` (`file_hash`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='已上传文件信息表(支持多云存储)';

设计要点:

  • file_hash 全局唯一标识,支持秒传和查重
  • storage_url 记录最终可访问路径
  • status 支持删除、归档等后续操作
  • 与业务系统中的文档解析、知识库构建直接关联
四、表关系架构

三张表的关系可简化为:

upload_task  (上传任务)
   ├── upload_chunk (分片详情)
   └── file_info    (最终文件)
  • upload_task 管理任务生命周期
  • upload_chunk 跟踪每个分片上传进度
  • file_info 保存最终文件索引,用于秒传与后续AI处理

这种设计确保:

  • 上传状态可追踪
  • 上传任务可恢复
  • 文件信息统一管理
  • 多节点部署一致性

状态管理与恢复机制

基于三张核心表,上传过程实现状态机化管理。每个上传任务从创建、上传、合并到完成都有明确状态,系统可在任意阶段中断后恢复,无需用户重新操作。

上传任务生命周期划分为关键状态:

WAITING(0, "待上传"),
UPLOADING(1, "上传中"),
MERGING(2, "合并中"),
COMPLETED(3, "已完成"),
CANCELED(4, "已取消"),
FAILED(5, "上传失败"),
CHUNK_MERGED(6, "已合并"),
PAUSED(7, "已暂停");
状态流转详解

WAITING(待上传) 用户发起上传、文件切片尚未传输时的初始状态。系统生成上传任务记录(/upload/init接口),任务仅登记在数据库,未开始数据传输。此时upload_task.status = 0,分片表无数据记录。

UPLOADING(上传中) 第一个分片开始上传时,任务状态更新为"上传中"。每上传一个分片,在upload_chunk表写入记录,并更新任务的uploaded_chunks字段。系统周期性根据分片数量更新进度条,支持任务恢复。

此阶段用户可能暂停、断网、刷新页面或浏览器崩溃,但分片信息已持久化到数据库,可随时通过upload_chunk状态恢复上传。

PAUSED(已暂停) 用户主动暂停上传时,任务状态标记为PAUSED。暂停不删除分片,仅阻止继续发送请求。用户点击"继续上传"时,前端获取未上传分片列表,实现断点续传。此状态通常在用户控制下出现,如网络切换场景。

CANCELED(已取消) 用户彻底放弃上传任务时标记为CANCELED。系统可选择删除临时分片文件或保留等待清理。后台日志记录取消操作的用户、时间和进度信息,用于审计追踪。

MERGING(合并中) 所有分片上传完成后,后端触发文件合并逻辑(调用/upload/merge)。任务状态切换为"MERGING",表示系统执行最终步骤。

合并过程:

  • 本地存储:逐个读取分片文件并拼接为完整文件
  • 云存储:触发服务端分片合并API(如OSS、MinIO)

大文件合并耗时较长,需单独设置状态标识。

CHUNK_MERGED(已合并) 部分系统将合并成功但未处理状态单独标识,用于文件校验或后处理。实际项目中可跳过此状态,直接进入COMPLETED。

COMPLETED(已完成) 文件合并完成、验证通过、存储路径落地、写入file_info表后,任务彻底完成。此状态下:

  • 用户可正常访问文件
  • 系统执行后续解析任务(文档拆页、向量化等)
  • 文件具备秒传条件

FAILED(上传失败) 上传过程出现异常(网络中断、磁盘写入异常、OSS上传失败等)时标记为FAILED。此状态不自动清理,便于管理员追踪错误原因或人工恢复。失败任务通常允许重新启动,从断点继续上传。

通过下图可直观了解上传任务生命周期:

图片

任务恢复实现机制

在此架构下,任务恢复变得自然流畅。前端每次进入上传页面时,传入文件hash,后端通过upload_taskupload_chunk判断:

  1. 文件是否存在上传任务
  2. 已上传分片列表
  3. 任务当前状态

前端仅需补传未完成分片,实现断点续传(Resumable Upload)。集群环境中,因任务与分片状态持久化在数据库,不依赖单台服务器,确保请求分发到任意机器都能保持进度一致性。

中断续传流程

实际使用中,上传中断常见场景包括:大文件上传中途浏览器关闭、网络中断、机器重启甚至更换设备继续操作。若无任务恢复机制,用户需重复传输整个文件,既浪费时间又易出错。

恢复机制核心 断点续传核心逻辑简单明了:任务和分片状态持久化存储。

用户重新选择相同文件时,前端计算文件hash并调用/upload/check接口。后端接收hash后依次查询三张表:

  1. 查询file_info:若存在记录,返回"文件已存在",实现秒传
  2. 查询upload_task:若找到对应任务,返回uploadId,表明上传中断
  3. 查询upload_chunk:统计已上传分片,返回未完成分片索引列表

前端获取这些信息后,从中断点继续传输,避免重复操作。

前端续传流程 前端获取旧uploadId和未完成分片列表后,跳过已上传分片,继续调用/upload/chunk上传剩余部分。上传过程中,每个分片状态实时更新至upload_chunk表,upload_task表的uploaded_chunks同步增加。全部分片上传完成后,任务自动进入MERGING状态。

续传过程本质是基于数据库状态的增量上传,用户无感知,系统自动恢复进度。

状态恢复判断 任务恢复权限基于upload_task.status判断:

状态 可恢复性 说明
WAITING 可恢复 任务刚创建,未开始传输
UPLOADING 可恢复 上传进行中,可继续
PAUSED 可恢复 用户主动暂停,可恢复
FAILED 可恢复 上传失败,可重新尝试
CANCELED 不可恢复 用户主动取消,不再恢复
COMPLETED 不需要 已完成,直接秒传
MERGING 等待中 系统合并中,前端等待

此判断逻辑确保任务行为清晰可控。用户暂停后返回可直接恢复,取消任务则不再自动续传。

集群部署兼容性 集群部署环境下,上传中断后续传可能请求到不同机器。因所有任务和分片状态持久化在数据库,不依赖内存或本地文件,即使上次上传在A机器,本次续传到B机器,系统仍能通过数据库记录识别uploadId下的分片状态,确保集群环境无缝续传。

机制优势 整个恢复机制依赖upload_task和upload_chunk两张表。upload_task记录任务总体进度,upload_chunk记录每个分片上传状态。用户重新上传时,系统查询进度状态,前端从断点继续,实现真正意义上的断点续传。

优势包括:

  • 上传进度可追踪
  • 中断后可恢复
  • 支持集群部署
  • 不依赖浏览器缓存或Session

只要数据库记录完整,上传进度即可恢复,跨设备、重启系统均不受影响。

文件合并与完整性校验

所有前期准备都是为了最终的文件合并环节。将分片拼接为完整文件并确保内容准确无误,是大文件上传流程中最易出错的阶段。特别是在集群部署环境中,若分片分布在不同机器,合并逻辑需特殊处理。

合并触发机制

前端检测所有分片上传完成后调用/upload/merge接口,通知后端开始合并操作。后端接收请求后验证关键信息:

  1. uploadId对应任务是否存在
  2. upload_chunk表中所有分片是否均为"已上传"状态
  3. 任务状态是否允许合并

验证通过后,任务状态从UPLOADING变更为MERGING,进入文件合并阶段。

本地合并流程

配置本地存储时(cloud.enable = false),所有分片文件保存在服务器临时目录。

合并逻辑:

  1. 按chunk_index顺序读取每个分片文件
  2. 流式写入新目标文件(如merge.zip)
  3. 每合并一个分片更新数据库状态
  4. 合并完成后更新任务状态为COMPLETED,记录最终路径

注意事项:

  • 写入顺序严格按分片索引,防止内容错乱
  • 使用流式写入(Stream)避免内存溢出

合并完成后计算文件MD5,与原始fileHash对比。不一致时标记任务为FAILED,记录异常日志。

云端合并方案

配置云存储(OSS、COS、MinIO等)时,分片文件上传至云端对象存储桶。此场景下无需本地拼接文件,直接调用云服务分片合并API。

以OSS为例:

  • 上传阶段调用uploadPart接口
  • 合并阶段调用completeMultipartUpload API
  • 云端按上传时分片顺序自动合并为完整对象

优势:

  • 不占用本地磁盘空间
  • 不受单机IO限制
  • 云端自动校验分片完整性

云存储场景仅需:

  1. 通知云服务执行合并
  2. 成功后记录storage_url到数据库
集群环境解决方案

单机环境下合并简单直接,所有分片位于本地目录。集群部署时,分片请求可能分发到不同节点,导致分片文件分散存储。

三种解决方案:

方案1:共享存储(推荐私有化部署) 所有机器上传目录指向共享路径(NFS、NAS或对象存储挂载至/data/uploads)。无论用户请求分发到哪台机器,分片均写入同一物理目录。合并请求可由任意节点处理,访问完整分片文件。这是企业部署中最稳定通用的方案。

方案2:云存储中转 机器间无共享目录时,分片先上传至云端,合并时调用云服务API。适用于公网SaaS环境,政企内网部署可能受限。

方案3:统一调度节点 自定义合并调度节点,所有分片上传完成后,合并任务分配至指定节点执行。该节点通过HTTP内部传输或RPC从其他机器拉取分片。适合大规模分布式存储场景,实现复杂度较高。

私有化项目通常采用方案1——共享目录+本地合并,兼顾性能与安全性。

完整性校验机制

文件合并完成后执行完整性校验。重新计算合并文件的MD5,与前端原始fileHash对比。一致则合并成功,内容完整;不一致则分片损坏或顺序错误,任务标记为FAILED,记录错误日志。确保后续AI解析或向量化阶段内容准确性。

异步处理优化

演示视频中采用同步执行方式,从前端上传分片到文件合并完成全程等待。虽然演示直观,但实际项目问题显著。

核心问题:耗时过长。1GB文件在稳定网络和服务器性能下仍需数分钟,若前端持续等待响应,可能遭遇接口超时、连接断开或页面刷新等问题。

业务系统中,通常将合并、校验、迁移OSS或解析入库操作改为异步任务。接口仅负责接收分片、登记状态,立即返回"任务已创建"或"上传完成,处理中"提示。后续操作交由后台异步线程、任务队列或调度器执行。

优势:

  • 前端体验流畅,无"等待合并"卡顿
  • 后端批量处理任务,降低高峰期IO压力
  • 任务失败或中断时可重试补偿
  • 自然衔接外部存储或AI解析流程

本质上,上传仅是第一步,合并、校验、转存等操作属后台任务。系统设计时分离这些环节,保持接口轻量,确保大文件复杂场景下的系统稳定性。

方案总结

合并与校验阶段是前期分片上传工作的收尾环节。通过以下机制保证稳定性:

  • 本地存储:顺序读取+流式写入+hash校验
  • 云存储:依赖云端分片合并API
  • 集群环境:共享存储或统一调度解决文件分散
  • 数据库:实时状态记录,支持追踪审计

文件合并成功、校验通过后,系统将结果写入file_info表,完成完整上传链路闭环。

架构总结

常规项目中文件上传功能通常简单直接,前端传OSS,后端存地址即可。但私有化项目环境要求截然不同,需综合考虑断点续传、任务恢复、集群部署、权限控制和审计追踪等要素。

本套设计通过明确接口职责、状态追踪和恢复机制,有效解决实际业务问题。接口数量稍多但职责清晰,任务状态全程可追踪,上传中断智能恢复,未来可独立抽离为文件系统模块。无论是知识库构建、AI向量化还是文档解析,这套架构都能稳定复用。

许多看似简单的功能在复杂场景下需重新设计,但一旦打通,就能长期稳定服务。大文件上传完整流程的实践经验,为后续类似需求提供了可靠技术基础。

图片

您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区(YunPan.Plus) ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-1 14:51 , Processed in 8.147577 second(s), 39 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 CloudStack.

快速回复 返回顶部 返回列表