文件上传
这篇文档说明 Geelato 当前文件上传能力的核心入口、存储方式、附件表模型,以及与 OSS 配置的关系。
本文主要基于:
UploadControllerFileHandlerUploadServiceAttachment
总体链路
当前最核心的上传入口是:
POST /api/upload/file
整体链路可以概括为:
UploadController接收上传请求和业务参数- 根据
tableType / tenantCode / appId / root / isRename计算保存路径 - 组装
FileParam FileHandler按serviceType决定走本地存储还是阿里云 OSS- 文件真正落盘或上传对象存储
- 通过
AccessoryHandler保存附件元数据 - 返回
Attachment
也就是说,框架把:
- 文件内容存储
- 附件元数据落库
拆成了两个阶段。
上传入口
文件上传
接口:
POST /api/upload/file
主要参数包括:
fileserviceTypetableTyperootisRenameobjectIdformIdsgenrebatchNoinvalidTimevalidDurationisThumbnailonlyThumbdimensionthumbScaleappIdtenantCode
其中最关键的是:
serviceType- 决定走本地还是 OSS
tableType- 决定属于哪类附件来源
root- 允许显式指定本地目录
isRename- 控制是否把原文件名改成 UID
配置对象上传
除了标准文件上传,还提供了:
POST /api/upload/objectPOST /api/upload/jsonPOST /api/upload/model/{entityName}/{id}
这些接口不是普通附件上传,而是把对象或 JSON 写入:
geelato.upload.config-directory
对应的配置文件目录。
文件存放位置
本地根目录
上传服务的本地目录由以下配置驱动:
geelato.upload.root-directorygeelato.upload.convert-directorygeelato.upload.config-directory
它们在 UploadService 初始化后会变成静态根路径:
rootDirectoryrootConvertDirectoryrootConfigDirectory
在默认 quickstart 配置里,常见值是:
geelato.upload.root-directory=/uploadgeelato.upload.convert-directory=/upload/convertgeelato.upload.config-directory=/upload/config
路径如何生成
对于标准附件上传,如果没有显式传 root,通常会走:
UploadService.getRootSavePath(...)
生成规则大致是:
- 以
geelato.upload.root-directory为根 - 拼接附件来源目录,例如
attach - 再拼接
tenantCode/appId - 再拼接日期目录
- 最后落到具体文件名
也就是说,本地附件目录通常会体现:
- 附件来源
- 租户
- 应用
- 日期
这样便于按业务来源和租户隔离文件。
如果显式传了 root
当上传请求带了:
root
就会改走:
UploadService.getSaveRootPath(root, fileName, isRename)
这意味着文件会直接落到你指定的根目录下,而不是默认的上传根目录结构。
本地与 OSS 两种存储模式
FileHandler 是真正的存储分发入口。
它的核心判断非常直接:
serviceType = aliyun时走 OSS- 否则走本地存储
本地存储
本地模式下:
- 文件内容写入本地磁盘
Attachment.path保存本地绝对路径Attachment.objectId通常为空
OSS 存储
OSS 模式下:
- 先通过
FileHelper.putFile(...)上传到阿里云 OSS - OSS 返回:
objectIdobjectName
- 再保存附件元数据
Attachment.path保存 OSS 对象名Attachment.objectId保存 OSS 对象 ID
所以在当前模型里:
path不一定是本地磁盘路径- 当
objectId非空时,通常表示该附件已经位于 OSS
缩略图能力
上传图片时可以开启:
isThumbnail
这会触发缩略图生成逻辑。
当前行为包括:
- 仅图片触发
- 支持多分辨率缩略图
onlyThumb=true时可以只保留缩略图- 缩略图和原图都会保存为附件记录
- 通过
pid建立父子关系
因此一张原图可能对应:
- 一条原图附件记录
- 多条缩略图附件记录
附件表与元数据
框架并不是把所有文件都简单塞进一个字段,而是维护一套附件元数据模型。
核心实体是:
Attachment
其中关键字段包括:
idpidappIdnametypegenresizepathobjectIdformIdsinvalidTimebatchNoresolution
这里要特别注意:
path- 本地模式下通常是磁盘路径
- OSS 模式下通常是对象名
objectId- OSS 对象标识
pid- 用于缩略图、引用、副本等父子关系
实际查询聚合的表
附件查询 SQL 并不是只查一个表,而是把三类表聚合起来:
platform_attachplatform_compressplatform_resources
对外统一映射成 Attachment 视图。
其中:
platform_attach- 普通附件
platform_compress- 压缩包附件
platform_resources- 资源型附件
并且 SQL 会根据 object_id 自动推导:
storageType = aliyunstorageType = local
这就是为什么附件列表接口可以直接按存储方式筛选。
OSS 配置
如果要启用阿里云 OSS,核心配置来自:
properties/oss.properties
关键配置项是:
geelato.oss.accessKeyIdgeelato.oss.accessKeySecretgeelato.oss.endPointgeelato.oss.bucketNamegeelato.oss.region
当前 quickstart 通过环境变量注入:
GEELATO_OSS_ACCESSKEYIDGEELATO_OSS_ACCESSKEYSECRETGEELATO_OSS_ENDPOINTGEELATO_OSS_BUCKETNAMEGEELATO_OSS_REGION
如果 OSS 未配置,而你又把 serviceType 指到 aliyun,FileHandler 会直接抛出明确错误,提示切回本地存储或补齐 OSS 配置。
上传后的常见附件管理接口
上传完成后,附件通常通过:
AttachController
继续管理,常见接口包括:
POST /api/attach/copy/{id}POST /api/attach/quote/{id}GET /api/attach/get/{id}POST /api/attach/update/{id}POST /api/attach/listPOST /api/attach/pageQueryDELETE /api/attach/remove/{id}POST /api/attach/storage/{type}GET /api/resources/file?id={id}&isPreview=true
其中:
storage/{type}- 支持本地和 OSS 之间切换存储
copy- 复制一份新文件
quote- 复用已有文件内容,仅新建引用记录
resources/file- 用附件
id获取文件内容 isPreview=true时按预览方式输出(通常用于图片/PDF 等)
- 用附件
使用建议
- 普通业务文件优先统一走
POST /api/upload/file - 本地开发环境可先使用本地存储,生产环境再切到 OSS
- 不要把 OSS 密钥写死在源码里,统一使用环境变量注入
- 如果需要缩略图,优先只对图片类型开启
- 业务上应把附件和自己的业务主数据通过
objectId / formIds / batchNo / genre等字段建立关联