3443 字
约 11 分钟
4
文件上传与文档处理全链路入门

文件上传与文档处理全链路入门

1. 整体流程

这套方案适合:

知识库
AI / RAG
企业文档中心
合同管理
全文检索
大文件上传平台

完整链路:

用户上传文件
    ↓
分片上传
    ↓
断点续传
    ↓
MinIO 保存分片
    ↓
文件合并
    ↓
Kafka
    ↓
文档解析
    ↓
文本分块
    ↓
Embedding
    ↓
Elasticsearch Vector
    ↓
语义搜索 / RAG

整个系统可以拆成六个模块:

分片上传与断点续传
        ↓
文件合并
        ↓
文档解析
        ↓
文本向量化
        ↓
异步处理
        ↓
文档管理

2. 分片上传与断点续传

什么时候使用?

普通几 MB 的图片没必要做分片。

如果需要上传:

500MB PDF
2GB ZIP
10GB 视频
大型 Office 文件

就适合采用分片上传。

核心目的:

大文件
 ↓
切成多个小 Chunk
 ↓
分别上传

网络断开以后,只重新上传:

没有成功的 Chunk

而不是整个文件重新上传。


技术方案

前端将文件按照例如:

5MB 左右 / Chunk

进行切分。

例如:

100MB 文件

↓

Chunk 0   5MB
Chunk 1   5MB
Chunk 2   5MB
...
Chunk 19  5MB

Fine Uploader 可以用于分片上传。

React Dropzone 更主要负责文件拖拽和选择,如果使用它,一般还需要自己通过:

file.slice(start, end)

实现真正的 Chunk 切分。

服务端:

Chunk
 ↓
MinIO

保存每一个分片。


Redis BitSet 记录上传状态

假设:

totalChunks = 10

Redis 可以记录:

0 1 2 3 4 5 6 7 8 9

已经上传:

0 ✅
1 ✅
2 ✅
3 ❌
4 ✅

BitSet 可以理解成:

1110100000

客户端重新连接以后查询:

哪些 Chunk 已经成功?

服务器告诉客户端:

0
1
2
4

客户端只需要继续:

3
5
6
7
8
9

这就实现了:

断点续传

文件唯一标识

按照你图里的设计:

File MD5

可以作为:

Redis Key
+
MinIO 路径的一部分

例如:

upload:{fileMd5}

MinIO:

temp/{fileMd5}/0.part
temp/{fileMd5}/1.part
temp/{fileMd5}/2.part

生产环境如果对碰撞安全要求较高,也可以进一步使用:

SHA-256

双重完整性校验

每个 Chunk 可以拥有:

chunkMd5

上传:

Chunk
+
chunkMd5

服务器重新计算:

客户端 MD5
==
服务端 MD5

确认分片没有损坏。

所有分片最终合并以后,再计算:

整个文件 MD5

实现:

分片校验
+
最终文件校验

每个分片上传成功以后,需要立即:

更新 Redis BitSet
+
更新数据库上传记录

3. 文件合并

当:

所有 Chunk 上传成功

或者:

客户端主动提交 Complete

就可以进入文件合并。

例如:

0.part
1.part
2.part
3.part
    ↓
Merge
    ↓
document.pdf

MinIO 合并

如果分片本身已经存在 MinIO,可以使用类似:

composeObject

的方式将多个对象组合为最终对象。

流程:

MinIO

0.part
1.part
2.part
3.part
    ↓
compose
    ↓
abc.pdf

这样可以避免:

MinIO
 ↓
下载到 Spring Boot
 ↓
本地合并
 ↓
重新上传 MinIO

大量没有必要的数据搬运。


合并前必须检查

不能简单判断:

分片数量 == 10

应该确认:

0.part ✅
1.part ✅
2.part ✅
...
9.part ✅

同时验证:

Chunk 数量
Chunk 顺序
Chunk 大小
Chunk Hash

全部正常以后再合并。


合并完成

成功之后:

更新数据库状态

UPLOADING
    ↓
MERGING
    ↓
SUCCESS

然后清理:

MinIO 临时 Chunk
+
Redis 临时状态

避免大量临时分片长期占用空间。

如果合并失败,需要保证:

最终文件不进入 SUCCESS

并能够重试或者执行补偿清理。

这里更准确地说是:

幂等 + 补偿式回滚

因为 MinIO、Redis、数据库之间并不存在一个真正统一的数据库事务。


4. 文档解析

文件合并完成后,下一步就是:

File
 ↓
Text

这一步就是文档解析。

适合:

知识库
全文搜索
RAG
合同分析
内容审核
文档问答

PDF

可以使用:

Apache PDFBox

例如:

contract.pdf
 ↓
PDFBox
 ↓
合同正文

适合需要针对 PDF 做比较深入处理的业务。


Word / Excel

使用:

Apache POI

处理:

DOC
DOCX
XLS
XLSX

例如:

report.xlsx
 ↓
POI
 ↓
Sheet
 ↓
Row
 ↓
Cell

可以获得表格数据。


通用文档

如果业务需求是:

不管用户上传 PDF、Word、PPT 还是其他文档,我主要想统一提取文本。

可以使用:

Apache Tika

架构:

PDF
Word
Excel
PPT
HTML
TXT
...
   ↓
Apache Tika
   ↓
统一 Text

因此三者可以这样理解:

PDFBox
→ 深度处理 PDF

POI
→ 深度处理 Office

Tika
→ 多格式统一解析

5. 加密文档

如果用户上传:

加密 PDF
密码保护 Word

普通解析流程会失败。

不要简单显示:

文件解析失败

而应该识别:

ENCRYPTED

然后:

提示用户输入密码
        ↓
重新提交解析任务
        ↓
验证密码
        ↓
解密并解析

形成一个交互式解密流程。


6. 保留文档结构

不要解析完以后只剩:

几十万字纯文本

最好尽量保留:

标题
章节
段落
页码
表格
列表

例如:

标题:
第三章 离职管理

段落:
员工离职以后需要关闭系统权限。

页码:
18

最后保存:

{
  "title": "第三章 离职管理",
  "page": 18,
  "content": "员工离职以后需要关闭系统权限。"
}

这样后面的:

搜索
RAG
结果引用

都会更容易。

复杂表格可以使用 POI、PDF 专用解析能力处理。

如果所谓“图表”实际上只有图片,没有底层文本或数据,就可能需要进一步结合:

OCR
视觉模型
专门表格识别

而不是单纯依靠 Tika。


7. 文本分块

完整文档不能直接拿去做一个 Vector。

例如:

300 页员工手册

里面同时存在:

入职
考勤
年假
薪资
离职
权限
报销

如果全部生成一个向量,语义会非常混乱。

因此:

Document
 ↓
Chunking
 ↓
Chunk 1
Chunk 2
Chunk 3
...

按照你图里的初始方案,可以:

每 512 字符

作为一个 Chunk。

例如:

Chunk 1
员工入职流程……

Chunk 2
员工年假规定……

Chunk 3
员工离职权限处理……

生产环境最好进一步考虑:

标题
段落
句子
+
最大长度

尽量不要从一句话中间直接切断。


8. 文本向量化

如果只做关键词搜索,到这里可以直接把 Chunk 存入 Elasticsearch。

如果需要:

语义搜索
RAG
相似文档搜索

就需要:

Embedding

按照你图里的方案:

Chunk
 ↓
豆包 Embedding API
 ↓
Vector

例如:

员工离职以后需要关闭系统账号

↓

Embedding API

↓

[0.18, -0.42, 0.76, ...]

这串数字就是:

Vector

9. 向量维度

向量维度不建议业务代码写死:

dimension = 1024;

而应该:

根据实际使用的 Embedding 模型

统一配置。

因为换模型以后:

Vector Dimension

也可能变化。

因此可以设计:

EmbeddingModel
    ↓
dimension
modelName
version

统一管理。


10. 批量向量化

不要:

Chunk 1 → 请求一次 API
Chunk 2 → 请求一次 API
Chunk 3 → 请求一次 API
...

如果模型 API 支持批量输入,更合理的是:

Chunk 1
Chunk 2
Chunk 3
Chunk 4
    ↓
Batch
    ↓
Embedding API
    ↓
Vector 1
Vector 2
Vector 3
Vector 4

可以降低大量 HTTP 请求带来的开销。

同时需要处理:

API Key
请求限流
超时
失败重试

11. 向量存储

按照你图中的当前方案:

Vector
 ↓
Elasticsearch

每个 Chunk 可以保存:

{
  "documentId": 1001,
  "chunkId": 5,
  "content": "员工离职以后需要关闭系统权限。",
  "vector": [0.12, -0.53, 0.88],
  "page": 18
}

Elasticsearch:

content
→ 关键词检索

vector
→ 向量检索

因此可以实现:

BM25
+
Vector Search

以后再扩展:

FAISS

作为本地高性能向量索引。

工程设计上最好提前抽象:

VectorStore

以后可以:

ElasticsearchVectorStore

FaissVectorStore

MilvusVectorStore

业务代码不需要跟着全部修改。

同时要做好:

Vector 序列化/反序列化
ES Vector Index 配置
任务状态
Embedding 异常处理

12. 为什么需要 Kafka 异步处理?

上传完成以后,不推荐:

HTTP 上传请求
     ↓
解析文件
     ↓
Chunk
     ↓
Embedding
     ↓
写 Elasticsearch
     ↓
最后才返回

因为整个过程可能非常慢。

推荐:

文件上传完成
     ↓
立即返回
     ↓
Kafka
     ↓
后台处理

例如:

File Upload Service
       ↓
Kafka
       ↓
Document Parser
       ↓
Embedding Worker

13. Kafka 任务拆分

文件上传完成:

Producer
   ↓
document-process Topic

消息:

{
  "documentId": 1001,
  "filePath": "documents/1001.pdf"
}

Consumer:

Kafka
 ↓
解析任务
 ↓
Tika / PDFBox / POI
 ↓
Chunk
 ↓
Embedding
 ↓
Elasticsearch

可以部署多个 Consumer:

Kafka
 ├─ Consumer 1
 ├─ Consumer 2
 ├─ Consumer 3
 └─ Consumer 4

并行处理不同文件,提高系统吞吐量。


14. Kafka 需要解决什么问题?

同一个文件可能存在:

解析
 ↓
分块
 ↓
向量化

这种前后依赖。

因此需要保证同一文件相关任务:

按正确顺序执行

例如 Kafka 可以考虑使用:

key = documentId

让同一文档相关消息进入相同 Partition。

任务失败以后:

失败
 ↓
Retry
 ↓
再次执行

超过重试次数:

DLQ / FAILED

同时数据库记录:

UPLOADED
PARSING
CHUNKING
EMBEDDING
SUCCESS
FAILED

管理后台就可以实时展示:

这个文档现在处理到哪一步了?

消费者数量也可以根据系统负载:

扩容
缩容

15. 文档管理

最终不要只考虑:

上传

还要考虑:

删除
权限

例如用户删除:

员工手册.pdf

不能只删除数据库的一条记录。

应该清理相关:

数据库 Document
        ↓
MinIO 原始文件
        ↓
Elasticsearch Chunk
        ↓
Vector
        ↓
临时 Chunk
        ↓
Redis 状态

否则就会留下大量垃圾数据。


16. 权限控制

按照你的设计:

普通用户
→ 只能删除自己的文档

管理员
→ 可以删除任何文档

例如:

DELETE /documents/1001

后端不能仅仅:

deleteById(1001);

而应该首先检查:

currentUserId
      ↓
document.ownerId

只有:

自己上传的文件

或者:

ADMIN

才允许执行删除。


17. 最终完整架构

把你图里的内容全部串起来,就是:

                     用户选择文件
                          ↓
                      文件分片
                          ↓
                 计算 File MD5
                          ↓
                 初始化 UploadTask
                          ↓
             ┌────────────┴────────────┐
             ↓                         ↓
       Redis BitSet                  MinIO
       记录分片状态                 保存 Chunk
             │                         │
             └────────────┬────────────┘
                          ↓
                      断点续传
                          ↓
                   Chunk MD5 校验
                          ↓
                     全部分片完成
                          ↓
                MinIO composeObject
                          ↓
                      文件总校验
                          ↓
                   清理临时 Chunk
                          ↓
                     更新数据库
                          ↓
                        Kafka
                          ↓
                    文档解析 Worker
             ┌────────────┼────────────┐
             ↓            ↓            ↓
          PDFBox         POI          Tika
             └────────────┼────────────┘
                          ↓
                       文本提取
                          ↓
                  标题 / 段落 / 表格
                          ↓
                      Text Chunk
                       ~512字符
                          ↓
                    Embedding Batch
                          ↓
                     豆包 API
                          ↓
                       Vector
                          ↓
                   Elasticsearch
               ┌──────────┴──────────┐
               ↓                     ↓
              BM25               Vector Search
               ↓                     ↓
           关键词检索               语义检索
               └──────────┬──────────┘
                          ↓
                    Hybrid Search
                          ↓
                       RAG / AI

18. 对照你原图

这版已经把图里的内容全部覆盖:

模块 已覆盖内容
分片上传与断点续传 前端分片、5MB、Redis BitSet、MinIO、断点续传、MD5、数据库状态、双重校验
文件合并 composeObject、合并条件、完整性/顺序检查、失败回滚思路、清理 Chunk、更新 DB/Redis、多规模文件策略
文档解析 PDFBox、POI、Tika、加密文档、标题/段落、复杂表格/图表、512 字符 Chunk
文本向量化 豆包 Embedding、维度适配、批量调用、ES Vector、FAISS 扩展、密钥/限流、序列化、索引、异常状态
异步处理 Kafka、生产者消费者、上传后异步分发、多 Consumer、顺序、重试、扩缩容、任务监控
文档管理 删除文档及关联数据、普通用户/管理员权限控制

一句话概括你这张图设计的系统:

它不是单纯的“文件上传系统”,而是一套从大文件可靠上传、对象存储、文档解析、文本向量化,到关键词/语义检索的完整文档处理流水线。

如果用于项目设计,我建议就把这六块直接作为六个核心模块来拆,后面的数据库表、Redis Key、Kafka Topic 和接口基本都能围绕这六块展开。

文件上传与文档处理全链路入门
http://www.clxhxhhr.top/posts/567/
作者
clxstart
发布于
2026-09-11
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。