# 完成分片上传并合并文件 ## 接口信息 - **接口名称**: 完成分片上传并合并文件 - **接口路径**: /file/completeUpload - **请求方法**: POST - **模块归属**: file - **版本号**: v1.0.0 - **创建日期**: 2026-01-18 - **最后更新**: 2026-01-18 ## 功能描述 完成分片上传并合并所有分片为一个完整的文件。需要提供所有分片的 ETag 信息,系统会验证并合并分片,同时在数据库中创建文件记录。 ## 请求参数 ### 表单参数 | 参数名 | 类型 | 必填 | 描述 | 示例 | |--------|------|------|------|------| | uploadId | String | 是 | 上传 ID,由初始化分片上传接口返回 | 1234567890abcdef | | filePath | String | 是 | 文件路径,由初始化分片上传接口返回 | /upload/2026/01/18/1234567890_large-file.zip | | fileSize | Long | 是 | 文件总大小(字节) | 1073741824 | | fileName | String | 是 | 文件名 | large-file.zip | ### 请求体 (JSON) ```json [ { "partNumber": 1, "eTag": "\"d41d8cd98f00b204e9800998ecf8427e\"" }, { "partNumber": 2, "eTag": "\"098f6bcd4621d373cade4e832627b4f6\"" } ] ``` | 参数名 | 类型 | 必填 | 描述 | 示例 | |--------|------|------|------|------| | partNumber | Integer | 是 | 分片序号 | 1 | | eTag | String | 是 | 分片的 ETag 值 | "d41d8cd98f00b204e9800998ecf8427e" | ## 响应数据 ### 成功响应 **HTTP 状态码**: 200 OK ```json { "code": 200, "message": "操作成功", "data": { "fileId": 1, "fileName": "large-file.zip", "filePath": "/upload/2026/01/18/1234567890_large-file.zip", "fileSize": 1073741824, "fileType": "zip", "storageType": "minio", "createBy": "admin", "createTime": "2026-01-18T10:00:00", "updateBy": "admin", "updateTime": "2026-01-18T10:00:00", "delFlag": "0" } } ``` | 字段名 | 类型 | 描述 | 示例 | |--------|------|------|------| | fileId | Long | 文件 ID | 1 | | fileName | String | 文件名 | large-file.zip | | filePath | String | 文件路径 | /upload/2026/01/18/1234567890_large-file.zip | | fileSize | Long | 文件大小(字节) | 1073741824 | | fileType | String | 文件类型 | zip | | storageType | String | 存储类型 | minio | | createBy | String | 创建人 | admin | | createTime | String | 创建时间 | 2026-01-18T10:00:00 | | updateBy | String | 更新人 | admin | | updateTime | String | 更新时间 | 2026-01-18T10:00:00 | | delFlag | String | 删除标志 | 0 | ### 失败响应 **HTTP 状态码**: 500 Internal Server Error ```json { "code": 500, "message": "分片信息不能为空", "data": null } ``` ```json { "code": 500, "message": "分片信息格式不正确", "data": null } ``` ```json { "code": 500, "message": "合并分片失败:未获取到最终文件路径", "data": null } ``` ## 接口示例 ### 请求示例 ```bash curl -X POST "http://localhost:8080/file/completeUpload" \ -H "Authorization: Bearer [token]" \ -H "Content-Type: application/json" \ -F "uploadId=1234567890abcdef" \ -F "filePath=/upload/2026/01/18/1234567890_large-file.zip" \ -F "fileSize=1073741824" \ -F "fileName=large-file.zip" \ -d '[ { "partNumber": 1, "eTag": "\"d41d8cd98f00b204e9800998ecf8427e\"" }, { "partNumber": 2, "eTag": "\"098f6bcd4621d373cade4e832627b4f6\"" } ]' ``` ### 响应示例 **成功**: ```json { "code": 200, "message": "操作成功", "data": { "fileId": 1, "fileName": "large-file.zip", "filePath": "/upload/2026/01/18/1234567890_large-file.zip", "fileSize": 1073741824, "fileType": "zip", "storageType": "minio", "createBy": "admin", "createTime": "2026-01-18T10:00:00", "updateBy": "admin", "updateTime": "2026-01-18T10:00:00", "delFlag": "0" } } ``` ## 错误处理 - 分片信息为空时返回错误 - 分片信息格式不正确时返回错误 - 分片序号或 ETag 无效时返回错误 - 合并分片失败时返回错误 - 未获取到最终文件路径时返回错误 ## 注意事项 - 分片信息必须按 partNumber 从小到大排序 - 分片序号必须从 1 开始,连续递增 - 所有分片的 ETag 都必须提供 - 合并成功后会自动在数据库中创建文件记录 - 使用系统配置的默认存储桶 - 文件类型从文件名中提取(扩展名) ## 相关接口 - [初始化分片上传](./0006-init-upload.md) - 初始化分片上传 - [上传文件分片](./0007-upload-chunk.md) - 上传文件分片 - [统一上传接口](./0002-upload.md) - 普通文件上传(非分片) ## 实现细节 - 使用 `StorageService.completeMultipartUpload()` 合并分片 - 验证并排序分片信息 - 过滤无效的分片信息 - 创建 `SysFileInfo` 实体并保存到数据库 - 使用 `SecurityUtils.getUsername()` 获取当前用户 - 文件类型从文件名的扩展名中提取 - 使用系统配置的默认存储桶