# upload-module
**Repository Path**: cyj000/upload-module
## Basic Information
- **Project Name**: upload-module
- **Description**: 分片上传模块
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-04-02
- **Last Updated**: 2026-04-02
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 分片上传模块(Chunk Upload Module)
基于 `rules/upload-module.mdc` 约定整理的分片上传通用代码,支持 **秒传**、**断点续传**、**分片上传与合并**。
可直接复制到新的 Spring Boot 项目中使用。
---
## 一、功能特性
| 功能 | 说明 |
|------|------|
| **秒传** | 通过 `identifier + fileName` 判断服务端是否已有完整文件,有则直接返回 URL。 |
| **断点续传** | 未传完时返回已上传的分片序号 `uploadedChunks`,前端只传缺失分片。 |
| **分片上传** | 按块上传,每块单独请求;全部传完后调用 **合并接口** 生成最终文件。 |
| **双端支持** | APP 使用 `MultipartFile`(`type=1`),PC/小程序使用 `byte[]`(`type=2`)。 |
| **安全检测** | 合并后可对 PDF 等文件做异步安全检测(示例已集成 PDF 恶意脚本检测)。 |
---
## 二、目录结构
```
upload-module/
├── README.md
├── sql/
│ └── a_file_manage.sql -- 建表脚本
└── src/main/java/com/ireql/upload/
├── controller/
│ └── UploadController.java -- 4 个核心接口(校验/APP上传/PC上传/合并)
├── dto/
│ └── FileDto.java -- 分片上传入参
├── vo/
│ └── FileVo.java -- 秒传/校验出参
├── entity/
│ └── FileManage.java -- 实体(对应 a_file_manage)
├── service/
│ ├── IFileManageService.java
│ └── impl/
│ └── FileManageServiceImpl.java
├── mapper/
│ ├── FileManageMapper.java
│ └── xml/
│ └── FileManageMapper.xml
├── response/
│ └── UploadResponse.java -- 合并后返回体
└── utils/
├── UploadResult.java -- 简易通用返回封装(可替换为你项目的 Result)
├── MimeTypeUtils.java -- 文件类型白名单
├── FileUploadUtils.java -- 上传工具(精简版)
├── CheckFileTypeUtil.java -- 文件后缀白名单校验
└── FileAuthUtil.java -- PDF 安全检测工具
```
---
## 三、快速集成步骤
### 3.1 复制代码
将 `upload-module/src/main/java/com/ireql/upload` 整体复制到你项目的源码目录下(例如 `src/main/java/com/xxx/upload`),并将包名批量替换为你项目的包名。
将 `upload-module/src/main/resources/mapper/FileManageMapper.xml` 复制到你项目的 `resources/mapper/` 下。
### 3.2 建表
执行 `sql/a_file_manage.sql` 创建 `a_file_manage` 表。
### 3.3 添加必要依赖
确保 `pom.xml` 中已包含以下依赖(版本号根据你项目调整):
```xml
com.baomidou
mybatis-plus-boot-starter
3.5.x
cn.hutool
hutool-all
5.8.x
org.apache.pdfbox
pdfbox
2.0.x
```
### 3.4 配置 application.yml
```yaml
upload:
# 对外访问的基础地址(例如你的域名或 IP)
baseUrl: http://127.0.0.1:8080
# 合并后文件的本地存放目录
path: D:/upload
# 临时分片文件的本地存放基目录
baseDir: D:/upload
# 资源前缀(决定对外 URL 中的前缀路径)
resourcePrefix: /profile
# uploadPath 减去 profile 的长度,用于裁剪生成相对路径
# 例如 uploadPath=D:/upload,profileLen=8("D:/upload".length())
profileLen: 8
```
### 3.5 MapperScan
在启动类或 MybatisPlusConfig 中增加 Mapper 扫描:
```java
@MapperScan({"com.xxx.upload.mapper", "..."})
```
### 3.6 安全/登录适配
- `UploadController` 中不再继承 `BaseController`,原 `getSysUser()` 获取登录用户的逻辑已注释掉。
- 请在你的项目中自行接入登录校验(如 Spring Security / JWT / Session),并将 `userId`、`nickName` 设置到 `FileDto` 中。
- 若使用 Spring Security,建议将 `/common/upload/**` 加入白名单或按你的认证规则放行。
### 3.7 替换统一返回对象(可选)
本模块使用 `UploadResult` 作为简易返回封装。如果你的项目已有统一的 `AjaxResult` / `Result`,可将代码中的 `UploadResult` 批量替换为你项目的返回类。
---
## 四、接口说明
> 前缀:`/common/upload`
| 接口 | 方法 | 入参 | 出参 | 职责 |
|------|------|------|------|------|
| 秒传校验 | `GET /verifyUrl` | `identifier`、`fileName` | `FileVo` | 判断是否已存在完整文件 |
| APP 分片上传 | `POST /uploadUrlByPhone` | `FileDto`(含 `MultipartFile file`) | `UploadResult` | `type=1` |
| PC 分片上传 | `POST /uploadUrlByPc` | `byte[] fileBin` + `fileName, index, chunkSize, totalChunks, identifier, totalSize` | `UploadResult` | `type=2` |
| 合并分块 | `GET /mergeUrl` | `identifier`、`fileName` | `UploadResult`(含最终 `url`) | 按序合并并清理临时分片 |
### 4.1 秒传校验返回示例
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"url": "http://127.0.0.1:8080/profile/upload/2024-01/xxx.png",
"needUpload": false,
"uploadedChunks": []
}
}
```
- `needUpload=false`:文件已存在,直接取 `url`。
- `needUpload=true`:需要继续上传,`uploadedChunks` 为已上传的分片序号列表。
### 4.2 合并返回示例
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"fileName": "xxx.png",
"oriName": "xxx.png",
"url": "http://127.0.0.1:8080/profile/upload/2024-01/xxx.png"
},
"url": "http://127.0.0.1:8080/profile/upload/2024-01/xxx.png"
}
```
---
## 五、核心约定(与 mdc 保持一致)
1. **唯一键**:`identifier + fileName` 唯一标识一个上传任务,对应一条父记录(`pid IS NULL`)。
2. **父子记录**:
- 父记录:存文件总信息,`pid = null`。
- 子记录:每个分片一条,`pid = 父id`,`fileIndex` 为分片序号,`path` 为临时分片本地路径。
3. **临时分片路径**:`{baseDir}/temp_file/{identifier}.{suffix}.{index}`
4. **合并后路径**:`{uploadPath}/{yyyy-MM}/{identifier}.{suffix}`
5. **合并时机**:仅当「已上传分片数 == totalChunks」时才执行物理合并。
6. **文件名支持 Base64**:接口内会先 `convertBase64(fileName)` 解码后再参与查库与类型校验。
---
## 六、注意事项
- `FileManageServiceImpl` 中的 `@Transactional` 保证了**写分片文件 + 写子记录**、以及**合并文件 + 更新父记录 + 删子记录**的原子性。
- PDF 安全检测目前是同步执行的;若文件较大或并发高,建议改为 `CompletableFuture.runAsync()` 异步执行,避免阻塞合并接口响应。
- 若不需要 PDF 检测,可直接移除 `FileAuthUtil` 及 `FileManageServiceImpl` 中的相关调用。
- 文件类型白名单在 `MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION` 中定义,可按业务需求扩展。
---
## 七、前端调用流程
1. **第一步**:调用 `GET /common/upload/verifyUrl` 做秒传/断点校验。
2. **第二步**:
- 若 `needUpload=false`,直接取 `url` 使用。
- 若 `needUpload=true`,根据 `uploadedChunks` 过滤出未上传的分片。
3. **第三步**:循环调用 `POST /common/upload/uploadUrlByPhone`(APP)或 `/uploadUrlByPc`(PC)上传缺失分片。
4. **第四步**:全部传完后调用 `GET /common/upload/mergeUrl` 合并,获取最终文件 URL。