文件上传(FileUploadController)¶
提供 KMS 知识管理模块「文件上传域」的能力:将一个或多个文件上传到指定网盘文件夹(含按目录结构上传整棵文件夹)。本控制器共 2 个端点。
注意:本控制器为 KMS 版(
@RestController(value="KmFileUploadController"),包cn.myapps.kms.controller),与运行时模块的UploadController(cn.myapps.kms.common.controller,见 upload.md)及 runtime 模块的同名控制器均不同——请勿混淆。Runtime Feign:表单附件
filePattern=02时,Runtime 经KmsApi先调folder.md#11 resolve-or-create 得folderId,再调本页 #1 上传文件(folderId+files;无diskId参数)。详见 runtime/file-upload.md。
- 接口类型:REST 资源(
@RestController,方法未声明produces,由全局消息转换器按 JSON 处理) - 基址:
${myapps.context-path.kms:}/api(类级@RequestMapping仅声明单一前缀;端点路径以/kms/...开头) - Tag:kms文件上传模块
公共说明¶
- 鉴权(据源码
KmsMvcConfig+KmsSecurityFilter):KmsMvcConfig注册全局 Servlet 过滤器KmsSecurityFilter(URL 模式/*,context-path 为/时为/kms/*)。本控制器路径/api/kms/upload、/api/kms/folder/upload不在KmsSecurityFilter.isExcludeURI的豁免名单内(豁免仅覆盖/login.*、/admin、/domain.*、/tray/service、/authtime、service/OfficeServer、.*outsideshare/.*/preview、静态资源后缀、actuator/health等;/kms/uploads下载路径虽豁免,但本控制器为/kms/upload、/kms/folder/upload上传路径,不在豁免范围)。过滤器调用Security.getUserIdFromToken(request),取不到用户则返回 HTTP401。因此所有端点均需 accessToken,可通过以下任一方式传递(据Security.getUserIdFromToken):query 参数accessToken、query 参数access_token(移动端)、请求头accessToken、CookieaccessToken、请求头Authorization: Bearer <token>。完整鉴权机制见 index.md「鉴权说明」。 - 执行用户:控制器内
getUser()(继承自AbstractBaseController)调用Security.getUserIdFromToken(request)还原当前用户 id,再经 Feign(UserAPI.getUserById)装载KmsUser,用于写入creator/creatorId/domainId。无userCode参数。 - 响应结构:统一
Resource(见 ../index.md「统一响应结构」),字段为errcode/errmsg/data/errors。 - 文件类型校验:上传时调用
cn.myapps.core.util.file.FileUtil.isLegalFileExt(extensions)校验扩展名;非法时本控制器**显式**返回errcode=4001(非默认错误码,见模块 index「错误码补充」)。 - 存储路径:文件按内容 MD5 校验和分桶存储:前 2 位为目录名、后 30 位为文件名,落盘至
${storageRootPath}/kms/.objects/<前2位>/<后30位>;上传前先写临时文件计算 checksum,再Files.copy到最终位置并删除临时文件。 - 异步后处理:上传完成后投递 KMS 后处理任务(摘要+关键词抽取、Lucene 索引、AI 向量化);任务投递失败仅记录日志、不影响上传结果。
- 错误处理:
folderId为空或multipartFiles.length == 0时抛MissingServletRequestPartException(HTTP 400);写盘异常包装为HttpMessageNotWritableException(HTTP 406)。
1. 上传文件¶
将一个或多个文件上传到指定的目标文件夹(folderId)。每个文件创建一条 FileEntity 记录(originType=UPLOAD),并投递索引建立任务。若传入 sourceFileId,则按源文件覆盖更新(保留原 Id、同步更新摘要记录、投递索引更新任务)。
- 接口类型:REST 资源
- 请求方式:
POST - 请求路径:
/kms/upload(完整:{kms-context}/api/kms/upload) - 鉴权:是(需 accessToken,据源码)
- Tag:kms文件上传模块
请求参数¶
| 参数名 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
| folderId | query/form | string | 是 | 目标文件夹Id(决定文件归属与网盘Id) |
| files | body(form) | file[] | 是 | 一个或多个文件(表单字段名固定为 files) |
| category | query/form | string | 否 | 标签名逗号分隔串(写入文件 categorys 字段) |
| sourceFileId | query/form | string | 否 | 源文件Id;传入时按源文件覆盖更新而非新建 |
请求示例¶
POST /api/kms/upload?folderId=__FOLDERID__&category=报告,审批&accessToken=__TOKEN__ HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
------WebKitFormBoundary
Content-Disposition: form-data; name="folderId"
__FOLDERID__
------WebKitFormBoundary
Content-Disposition: form-data; name="files"; filename="报告.docx"
Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document
(二进制内容)
------WebKitFormBoundary--
响应¶
结构:统一 Resource。
data:List<Map>,每项结构如下:
| 字段 | 类型 | 说明 |
|---|---|---|
| fileName | string | 原始文件名 |
| filePath | string | 上传后路径前缀+相对路径(FileUtil.UPLOAD_FOLDER_PATH + url) |
| id | string | 文件Id(新建为随机 UUID;覆盖更新时为源文件Id) |
| fileSize | long | 文件大小(字节) |
| fileType | string | 含 . 的扩展名(如 .docx) |
成功示例:
{
"errcode": 0,
"errmsg": "ok",
"data": [
{ "fileName": "报告.docx", "filePath": "/kms/uploads/a3/abcdef...", "id": "__FILEID__", "fileSize": 10240, "fileType": ".docx" }
],
"errors": null
}
失败示例(文件类型不合法,显式返回 4001):
失败示例(文件为空):抛 MissingServletRequestPartException(HTTP 400)。
2. 上传文件夹¶
按前端拖拽上传文件夹场景,将一组文件连同其目录结构(filesPath)整体上传到指定网盘下。服务端会按最深目录逐层创建 FolderEntity,再将每个文件归位到对应子文件夹。
- 接口类型:REST 资源
- 请求方式:
POST - 请求路径:
/kms/folder/upload(完整:{kms-context}/api/kms/folder/upload) - 鉴权:是(需 accessToken,据源码)
- Tag:kms文件上传模块
请求参数¶
| 参数名 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
| folderId | query/form | string | 是 | 目标根文件夹Id(目录树挂载点) |
| diskId | query/form | string | 是 | 网盘Id |
| files | body(form) | file[] | 是 | 一个或多个文件(表单字段名固定为 files) |
| filesPath | query/form | string[] | 是 | 与 files 一一对应的目录结构路径(前端拖拽上传时携带,用于重建目录树) |
说明:服务端将
filesPath[i]与files[i].getOriginalFilename()配合解析目录层级;文件名以~开头(Office 临时文件)会被跳过。
请求示例¶
POST /api/kms/folder/upload?folderId=__FOLDERID__&diskId=__DISKID__&filesPath=/项目/2024/报告.docx&accessToken=__TOKEN__ HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
------WebKitFormBoundary
Content-Disposition: form-data; name="folderId"
__FOLDERID__
------WebKitFormBoundary
Content-Disposition: form-data; name="diskId"
__DISKID__
------WebKitFormBoundary
Content-Disposition: form-data; name="filesPath"
/项目/2024/报告.docx
------WebKitFormBoundary
Content-Disposition: form-data; name="files"; filename="报告.docx"
Content-Type: application/octet-stream
(二进制内容)
------WebKitFormBoundary--
响应¶
结构:统一 Resource。
data:Map,结构如下:
| 字段 | 类型 | 说明 |
|---|---|---|
| msg | string | 固定为 "上传成功" |
| id | string | 最深层目录创建后的目标文件夹Id(copyFolderId,便于前端继续操作) |
| autoCategoryList | array | 各文件元信息列表,每项结构同 #1 响应 data 元素(fileName/filePath/id/fileSize/fileType) |
成功示例:
{
"errcode": 0,
"errmsg": "ok",
"data": {
"msg": "上传成功",
"id": "__FOLDERID2__",
"autoCategoryList": [
{ "fileName": "报告.docx", "filePath": "/kms/uploads/a3/abcdef...", "id": "__FILEID__", "fileSize": 10240, "fileType": ".docx" }
]
},
"errors": null
}
失败示例(文件类型不合法):