文件上传与文档处理全链路入门
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
合同分析
内容审核
文档问答
可以使用:
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 和接口基本都能围绕这六块展开。