跳转至

表单文件上传执行(FileUploadController)

表单附件/图片上传执行接口:文件上传(三种落盘:系统默认 / 本地路径 / 企业网盘)、文件排序、删除文件、获取历史版本、恢复历史版本、生成历史版本、按用户分页获取关联文件。基址为 /api(继承自 AbstractRuntimeController@RestController,方法返回统一 Resource)。

落盘模式**由上传字段 AbstractUploadField.filePattern(下发属性名 fileSaveMode)决定;01/02 的目标路径由服务端执行字段 **pathScript(目标路径脚本) 计算,不下发脚本源码到前端。设计说明见仓库 docs/superpowers/specs/2026-08-06-upload-field-store-place-design.md

  • 接口类型:REST 资源(@RestController 继承 AbstractRuntimeController,方法返回 Resource,由 Spring 以 JSON 序列化输出)
  • 基址${myapps.context-path.runtime:}/api
  • Tag:表单文件上传执行模块
  • 源码cn.myapps.runtime.dynaform.form.controller.FileUploadController

公共说明

  • 鉴权(据源码 RuntimeMvcConfig + RestSecurityHandlerInterceptor:本控制器所有方法级路径均位于 /api/runtime/** 下(类级 /api + 方法级 /runtime/...),在 RestSecurityHandlerInterceptor 覆盖范围内,且不在豁免名单(豁免名单见 login.md「公共说明 · 鉴权」)。所有端点均需 accessToken(或 debugToken),可通过 Cookie / 请求头 / query 参数任一方式传递;执行用户从令牌还原,无 userCode 参数。控制器内 getUser() 必须能取到非空 WebUser,否则在 DesUtil.decryptTextByUserId(applicationId, getUser().getId()) 处即抛 NPE。
  • 路径变量 {applicationId}:经 DES 加密(按当前执行用户密钥),服务端在方法入口处 DesUtil.decryptTextByUserId(applicationId, getUser().getId()) 解密。
  • 响应结构:除特别注明外,所有端点返回统一 Resource(见 ../index.md「统一响应结构」)。
  • HTTP 状态码:所有端点类级标注 @ResponseStatus(HttpStatus.OK),成功统一返回 200;业务错误由响应体 errcode 体现。
  • 文件上传安全:上传文件名禁止包含 /\;扩展名须经 FileUtil.isLegalFileExt(extName) 校验,否则返回 error(4001, "上传的文件类型不合法!", null)
  • 落盘意图:有绑定表单字段时,以字段上的 filePattern 为准(忽略客户端伪造的 fileSaveMode/path 用于 01/02 实际存储);无字段时回退 query 的 fileSaveMode(默认 00)。

落盘模式(filePattern / fileSaveMode

含义 行为
00 系统默认 不执行 pathScript;落盘 {storageRoot}/uploads/item/{应用名}/{年}/{月}/...(沿用 ITEM_PATH
01 本地路径 服务端执行 pathScript → 相对路径 rel;物理路径 {storageRoot}/{rel}/...;返回相对 storageRootfilePath
02 企业网盘 服务端执行 pathScript → Feign resolve-or-create → Feign /kms/upload;使用 KMS 返回的 id/filePath;无本地实体文件时跳过依赖本地路径的转换任务

路径规范化(01/02):统一 /;去首尾空白与多余 /;禁止 .. 段(否则 errcode=4001路径不允许包含..)。pathScript 未配置或脚本结果为空 → 4001


1. 文件上传

接收 multipart 文件数组,按字段 filePattern(及 pathScript)落盘到系统默认目录、本地自定义相对路径,或经 Feign 归档到企业网盘。返回每个文件的 {fileName, filePath, fileSize, fileType, id} 数组。

  • 接口类型:REST 资源(multipart/form-data 文件上传)
  • 请求方式POST
  • 请求路径/runtime/upload(完整:{runtime-context}/api/runtime/upload
  • 鉴权:是(需 accessToken,据源码)
  • Tag:表单文件上传执行模块

请求参数

参数名 位置 类型 必填 说明
applicationId query string 软件 id(DES 加密密文)
allowedTypes query string 允许上传类型(如 imagefile
fieldId query string 表单字段 id(拼接 docId 后用于定位字段名)
fileSaveMode query string 文件保存模式(00/01/02);有字段时以字段 filePattern 为准
path query string 上传路径占位;00 时参与目录计算,01/02 时真实路径仅由服务端 pathScript 决定
actionType query string 类型;为 excelImport 时不执行 swf 转换
sourceFileId query string 源文件 id(用于版本延续,自动递增 V0.x
docId query string 文档 id(DES 加密密文,用于绑定表单与摘要);01/02 必填(需加载文档以执行脚本)
files body (form-data) MultipartFile[] 多个文件

请求示例(00 系统默认)

POST /api/runtime/upload?applicationId=__ENC_APPID__&allowedTypes=file&fieldId=__FIELDID__&fileSaveMode=00&path=/uploads/item&actionType=upload&docId=__ENC_DOCID__ HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
Cookie: accessToken=eyJhbGciOiJIUzI1NiJ9...

------WebKitFormBoundary
Content-Disposition: form-data; name="files"; filename="demo.png"
Content-Type: image/png

<二进制内容>
------WebKitFormBoundary--

请求示例(01/02

path 可传空或占位;落盘目录由字段 pathScript 在服务端计算。02 时 Runtime 先调 KMS resolve-or-create,再调 upload(folderId, file)

POST /api/runtime/upload?applicationId=__ENC_APPID__&allowedTypes=file&fieldId=__DOCID___attachment&fileSaveMode=02&path=&actionType=upload&docId=__ENC_DOCID__ HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
Cookie: accessToken=eyJhbGciOiJIUzI1NiJ9...

------WebKitFormBoundary
Content-Disposition: form-data; name="files"; filename="report.docx"
Content-Type: application/octet-stream

<二进制内容>
------WebKitFormBoundary--

响应

结构:统一 Resource(见 ../index.md「统一响应结构」)。 dataJSONArray,每个元素为 { id, fileName, filePath, fileSize, fileType }

{
  "errcode": 0,
  "errmsg": "ok",
  "data": [
    {
      "fileName": "demo.png",
      "filePath": "/uploads/item/DemoApp/2026/8/demo-xxx.png",
      "fileSize": 102400,
      "fileType": ".png",
      "id": "demo-xxx"
    }
  ],
  "errors": null
}

失败示例

{ "errcode": 4001, "errmsg": "上传的文件类型不合法!", "data": null, "errors": null }
{ "errcode": 4001, "errmsg": "数据异常,请重新创建或者保存后重试!", "data": null, "errors": null }
{ "errcode": 4001, "errmsg": "目标路径脚本未配置", "data": null, "errors": null }
{ "errcode": 4001, "errmsg": "目标路径脚本结果为空", "data": null, "errors": null }
{ "errcode": 4001, "errmsg": "路径不允许包含..", "data": null, "errors": null }
{ "errcode": 400, "errmsg": "企业网盘数量异常,期望唯一企业盘", "data": null, "errors": null }

02 时 KMS Feign 失败会透传对方 errcode/errmsg(如企业盘数量异常为 400)。


2. 文件排序

按 JSON 请求体的路径定位 applicationId/formId/docId/itemName/itemValue,调用 DocumentProcess.updateFileSorting 更新指定字段的文件顺序,返回排序后的文件列表。

  • 接口类型:REST 资源
  • 请求方式POST
  • 请求路径/runtime/files/sort(完整:{runtime-context}/api/runtime/files/sort
  • 鉴权:是(需 accessToken,据源码)
  • Tag:表单文件上传执行模块

请求参数

参数名 位置 类型 必填 说明
content body string(JSON) 请求包体,含下表字段
请求体字段
字段 类型 必填 说明
applicationId string 软件 id(DES 加密密文)
formId string 表单 id
docId string 文档 id(DES 加密密文)
itemName string 字段名
itemValue string 字段值(含排序后文件清单)
{
  "applicationId": "__ENC_APPID__",
  "formId": "__FORMID__",
  "docId": "__ENC_DOCID__",
  "itemName": "attachment",
  "itemValue": "..."
}

请求示例

POST /api/runtime/files/sort HTTP/1.1
Content-Type: application/json
Cookie: accessToken=eyJhbGciOiJIUzI1NiJ9...

{ "applicationId": "__ENC_APPID__", "formId": "__FORMID__", "docId": "__ENC_DOCID__", "itemName": "attachment", "itemValue": "..." }

响应

结构:统一 ResourcedataJSONArray,排序后 Document 字段反序列化得到的 datas 数组。

{ "errcode": 0, "errmsg": "ok", "data": [{ "id": "demo-xxx", "fileName": "demo.png" }], "errors": null }

3. 删除文件

path 定位物理文件并删除,同时移除 UploadVO 元数据。path 中以 _/uploads 分隔前缀作为 fileId,否则按 PATH 列查询。

  • 接口类型:REST 资源
  • 请求方式DELETE
  • 请求路径/runtime/files/delete(完整:{runtime-context}/api/runtime/files/delete
  • 鉴权:是(需 accessToken,据源码)
  • Tag:表单文件上传执行模块

请求参数

参数名 位置 类型 必填 说明
applicationId query string 软件 id(DES 加密密文)
docId query string 文档 id(DES 加密密文,源码对该参数未单独 DES 解密,仅作为请求入参)
path query string 文件保存路径(需 URL 编码,服务端 URLDecoder 解码)
formId query string 表单 id

请求示例

DELETE /api/runtime/files/delete?applicationId=__ENC_APPID__&docId=__ENC_DOCID__&path=%2Fuploads%2Fdemo%2Fdemo-xxx.png&formId=__FORMID__ HTTP/1.1
Cookie: accessToken=eyJhbGciOiJIUzI1NiJ9...

响应

结构:统一 Resourcedatastring,固定为 "删除成功"(即便 path 为空也返回成功)。

{ "errcode": 0, "errmsg": "ok", "data": "删除成功", "errors": null }

失败示例(文件存在但删除失败抛 OBPMValidateException):

{ "errcode": 500, "errmsg": "File(<realPath>) delete failed", "data": null, "errors": null }


4. 获取文件历史版本信息

fileId 沿 sourceFileId 链向上回溯,返回该文件的全部历史版本(不含当前版本)。

  • 接口类型:REST 资源
  • 请求方式GET
  • 请求路径/runtime/{applicationId}/files/{fileId}/versions(完整:{runtime-context}/api/runtime/{applicationId}/files/{fileId}/versions
  • 鉴权:是(需 accessToken,据源码)
  • Tag:表单文件上传执行模块

请求参数

参数名 位置 类型 必填 说明
applicationId path string 软件 id(DES 加密密文)
fileId path string 文件 id

请求示例

GET /api/runtime/__ENC_APPID__/files/__FILEID__/versions HTTP/1.1
Cookie: accessToken=eyJhbGciOiJIUzI1NiJ9...

响应

结构:统一 ResourcedataList<UploadVO>,沿 sourceFileId 链回溯到的历史版本集合(无历史时为空数组)。

{
  "errcode": 0,
  "errmsg": "ok",
  "data": [
    { "id": "__FILEID_V1__", "name": "demo.png", "versionNo": "V1.0", "sourceFileId": null }
  ],
  "errors": null
}

5. 恢复文件历史版本

recoverFileId 指定的文件克隆一份新记录(versionNo 在 sourceFileId 的基础上 +0.1),并复制物理文件与对应 swf/pdf 产物,作为「恢复」出的新版本。

  • 接口类型:REST 资源
  • 请求方式POST
  • 请求路径/runtime/{applicationId}/files/{recoverFileId}/versions/recover(完整:{runtime-context}/api/runtime/{applicationId}/files/{recoverFileId}/versions/recover
  • 鉴权:是(需 accessToken,据源码)
  • Tag:表单文件上传执行模块

请求参数

参数名 位置 类型 必填 说明
applicationId path string 软件 id(DES 加密密文)
recoverFileId path string 待恢复的目标文件 id
sourceFileId query string 源文件 id(用于取上一版本号)

请求示例

POST /api/runtime/__ENC_APPID__/files/__RECOVERFILEID__/versions/recover?sourceFileId=__SOURCEFILEID__ HTTP/1.1
Cookie: accessToken=eyJhbGciOiJIUzI1NiJ9...

响应

结构:统一 ResourcedataUploadVO,新克隆生成的版本记录(含 id/versionNo/path/sourceFileId 等字段)。

{
  "errcode": 0,
  "errmsg": "ok",
  "data": {
    "id": "demo-xxx-new",
    "name": "demo.png",
    "versionNo": "V1.2",
    "sourceFileId": "__SOURCEFILEID__"
  },
  "errors": null
}

6. 生成文件历史版本

fileId 指定的当前文件克隆出一份历史版本(新 versionNo +0.1),同时把当前文件的 sourceFileId 指向新版本,复制底层物理文件。

  • 接口类型:REST 资源
  • 请求方式POST
  • 请求路径/runtime/{applicationId}/files/{fileId}/versions/build(完整:{runtime-context}/api/runtime/{applicationId}/files/{fileId}/versions/build
  • 鉴权:是(需 accessToken,据源码)
  • Tag:表单文件上传执行模块

请求参数

参数名 位置 类型 必填 说明
applicationId path string 软件 id(DES 加密密文)
fileId path string 文件 id

请求示例

POST /api/runtime/__ENC_APPID__/files/__FILEID__/versions/build HTTP/1.1
Cookie: accessToken=eyJhbGciOiJIUzI1NiJ9...

响应

结构:统一 ResourcedataList<UploadVO>,源码恒为 new ArrayList<>()(即空数组,仅表示操作成功)。

{ "errcode": 0, "errmsg": "ok", "data": [], "errors": null }

7. 获取用户相关文件信息

分页查询当前登录用户上传的文件列表,可按 fileNamecustomizeType 过滤。

  • 接口类型:REST 资源
  • 请求方式GET
  • 请求路径/runtime/{applicationId}/files/relevances(完整:{runtime-context}/api/runtime/{applicationId}/files/relevances
  • 鉴权:是(需 accessToken,据源码)
  • Tag:表单文件上传执行模块

请求参数

参数名 位置 类型 必填 说明
applicationId path string 软件 id(DES 加密密文)
pageNo query int 页码,默认 1
linesPerPage query int 每页行数,默认 10
fileName query string 文件名模糊过滤
customizeType query string 文件格式过滤(扩展名)

请求示例

GET /api/runtime/__ENC_APPID__/files/relevances?pageNo=1&linesPerPage=10&fileName=demo&customizeType=.png HTTP/1.1
Cookie: accessToken=eyJhbGciOiJIUzI1NiJ9...

响应

结构:统一 ResourcedataDataPackage<UploadVO>,分页结果(含 datas/pageCount/pageNo/linesPerPage/rowCount)。

{
  "errcode": 0,
  "errmsg": "ok",
  "data": {
    "datas": [{ "id": "__FILEID__", "name": "demo.png", "path": "/uploads/demo/demo-xxx.png" }],
    "pageNo": 1,
    "linesPerPage": 10,
    "pageCount": 1,
    "rowCount": 1
  },
  "errors": null
}