ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Strapi 如何开发自定义 Upload Provider 对接自有对象存储(upload/delete/getSignedUrl)?

2026/9/12 17:03:02 拓冰建站 浏览量
Strapi 如何开发自定义 Upload Provider 对接自有对象存储(upload/delete/getSignedUrl)? Strapi 如何开发自定义 Upload Provider 对接自有对象存储upload/delete/getSignedUrl【免费下载链接】strapi Strapi is the leading open-source headless CMS. It’s 100% JavaScript/TypeScript, fully customizable, and developer-first.项目地址: https://gitcode.com/GitHub_Trending/st/strapi如果你有自有的对象存储希望 Strapi 中上传的媒体文件不再写到本地uploads/目录而是落到你的存储里就需要开发一个自定义 Upload Provider。完成本文任务后Strapi 上传的媒体会写入你的对象存储删除媒体时会同步删除对象私有存储还能通过签名 URL 对外提供访问。本文依据仓库内的 Provider 契约与官方加载逻辑契约见 Provider 文档加载逻辑在 register.ts官方 S3 provider 与 local provider 可作为参考实现。前提一个可用的 Strapi 项目upload 插件已启用以及你自己的对象存储服务及其 SDK 或 HTTP API。Provider 契约要实现哪些方法官方文档的定义是Provider 是一个包它导出一个带init函数的对象Strapi 启动时调用init(options)返回的对象就是 provider 实例。各方法的职责引自 00-providers.md方法要求职责upload(file)与uploadStream至少实现一个把文件上传到 provideruploadStream(file)与upload至少实现一个推荐把流上传到 providerdelete(file)必须从 provider 删除文件isPrivate()可选返回存储是否私有默认false为真时改用getSignedUrl获取文件 URLgetSignedUrl(file)可选存储需要鉴权时返回访问文件的签名 URLStrapi 在启动阶段校验实例register.ts对应报错如下无法解析 provider 模块Could not load upload provider xxx.缺少deleteThe upload provider xxx doesnt implement the delete method.upload与uploadStream都缺失The upload provider xxx doesnt implement the uploadStream nor the upload method.只缺uploadStream不致命仅警告The upload provider xxx doesnt implement the uploadStream function. Strapi will fallback on the upload method. Some performance issues may occur.前三条决定项目能否启动最后一条影响运行期性能所以建议把uploadStream也实现掉。Strapi 如何找到并调用你的 provider配置写在./config/plugins.jsconst pluginConfig { upload: { // provider 名称会被转成小写后使用 provider: my-oss, // 整个对象会作为参数传给 provider 的 init(options) providerOptions: { // 你自己的对象存储参数例如 bucket、endpoint、凭据 }, }, };加载逻辑register.ts读取plugin::upload配置取provider名称并转小写先尝试解析 npm 包strapi/provider-upload-名称若未安装MODULE_NOT_FOUND则直接require你配置的这个名字——也就是说 provider 既可以是安装的包也可以是项目内可被引用的模块调用provider.init(providerOptions)得到实例。之后 Strapi 对实例上的每个函数做包装register.ts调用同名方法时会把配置里actionOptions.方法名作为第二个参数传入。也就是说你可以在 upload 配置里声明actionOptions: { upload: {...} }按方法名给不同方法传参方法签名写成(file, options)即可。实例还继承baseProvider的几个默认实现isPrivate()默认返回falsegetSignedUrl(file)默认原样返回文件checkFileSize(file, { sizeLimit })在文件超过 upload 配置中的sizeLimit时抛出PayloadTooLargeError。Provider 收到的 File 对象file是 Strapi 组装好的媒体记录provider 实现中常用到的字段定义见 S3 provider 的Fileindex.tsfile.hash/file.ext文件哈希与扩展名官方 provider 都用它们拼对象 Keyfile.stream流式路径或file.buffer缓冲路径文件内容file.mimeMIME 类型写入时作为 Content-Typefile.path目录路径S3 provider 会把它拼进 Key 前缀file.urlprovider 必须在这里写回文件的访问地址Strapi 最终保存的媒体 URL 就是你设置的值。对象 Key 的拼法参考两个官方实现local provider 直接用${file.hash}${file.ext}S3 provider 按rootPath前缀 →file.path→hashext的顺序拼接getFileKey。实现 uploadStreamlocal provider 的实现upload-local/src/index.ts展示了最小模式消费file.stream写入存储成功后写回file.urluploadStream(file: File): Promisevoid { if (!file.stream) { return Promise.reject(new Error(Missing file stream)); } return new Promise((resolve, reject) { pipeline( file.stream, fs.createWriteStream(path.join(uploadPath, ${file.hash}${file.ext})), (err) { if (err) return reject(err); file.url /uploads/${file.hash}${file.ext}; resolve(); } ); }); }S3 provider 的写法upload请求体为file.stream || Buffer.from(file.buffer, binary)ContentType 取file.mime上传完成后把访问 URL 写回file.url把ETag去掉引号后写入file.etag。你的对象存储返回的 URL 拼接方式可参考它的优先级配置了baseUrlCDN 或自定义域名时优先用它其次使用存储返回的地址兜底用endpoint/bucket/key拼接。实现 deletedelete(file)按同样的规则算出 Key然后删除对象即可。S3 provider 用DeleteObjectCommand按Bucket Key删除delete。官方文档明确要求 provider should be able to upload files to a remote server and delete them——即 Strapi 删除媒体时你存储里的对象也要随之移除。私有文件与 getSignedUrl如果存储不允许匿名访问让isPrivate()返回true。URL 签名流程在 file.ts 中从 provider 读取isPrivate若为false或文件自身的provider字段与当前配置的 provider 名称不一致直接返回原文件不签名否则调用getSignedUrl(file)把返回结果里的url写进文件的url并标记file.isUrlSigned truefile.formats里的每个尺寸响应式图片也会逐个签名。因此getSignedUrl必须返回带url字段的对象。S3 provider 的实现getSignedUrl用 presigner 对GetObjectCommand生成临时 URLexpiresIn取配置params.signedUrlExpires默认15 * 60秒返回{ url }。自定义 provider 的最小骨架综合以上契约对接自有对象存储的 provider 骨架如下。// TODO行是需要替换为你自己的对象存储 SDK 调用的部分其余结构可直接保留// 自定义对象存储 provider export default { init(options) { // options 就是 ./config/plugins.js 里的 providerOptions // 对象 Key官方惯例为 hash ext需要目录前缀时可自行拼接 const buildKey (file) ${file.hash}${file.ext}; return { // 推荐实现流式写入比缓冲路径性能好 uploadStream(file) { const key buildKey(file); // TODO: 把 file.stream 以 key 写入你的对象存储 // TODO: 成功后把对象的访问 URL 写入 file.url }, // 可选缓冲路径只实现 uploadStream 时可以不写 upload upload(file) { // TODO: 把 file.buffer 以 buildKey(file) 写入你的对象存储并写回 file.url }, // 必须实现 delete(file) { // TODO: 从你的对象存储删除 buildKey(file) 对应的对象 }, // 存储私有时返回 true启用 URL 签名 isPrivate() { return true; }, // 仅当 isPrivate() 为 true 时被调用必须返回 { url } async getSignedUrl(file) { // TODO: 用你的存储签名机制为对象生成临时访问 URL // 形如return { url: await signObject(buildKey(file)) }; }, }; }, };把该模块放到 provider 名称能解析到的位置安装为 npm 包或项目中可require的模块路径再在./config/plugins.js中按上文格式配置。验证方式Strapi 启动后逐项核对启动校验provider 包解析不到、delete缺失或upload/uploadStream都缺失时启动会抛出第一节列出的对应错误能正常启动说明基础契约满足。若看到uploadStream警告说明流式实现还没补齐。上传链路在管理端或调用上传接口做一次媒体上传返回的媒体url应正是你的 provider 写入file.url的地址你的对象存储里应出现 Key 为${hash}${ext}的新对象。删除链路删除该媒体后你存储中对应的对象应不再存在。签名 URLisPrivate()返回true、且媒体的provider与配置的 provider 名称一致时API 返回的媒体url应是被替换后的签名 URLisUrlSigned字段为true。可选分支对象存储兼容 S3 协议时如果你的自有对象存储实现了 S3 协议可以不写自定义 provider直接使用官方strapi/provider-upload-aws-s3把s3Options的endpoint指向你的存储即可。源码中有几点限制需要注意params.Bucket必填否则启动报错Upload AWS S3 provider: \params are required in the config objectendpoint、credentials等要放进s3Options对象里平铺在providerOptions根级别是已弃用的方式会触发告警当 endpoint 不是 AWS 时storageClass、非AES256的加密等 AWS 专属配置可能被你的存储忽略Strapi 会发出告警例如Storage class STANDARD_IA is AWS S3-specific and may be ignored by your S3-compatible provider.。限制说明provider 名称会被_.toLower转小写后再解析配置名与实际包名注意保持一致URL 签名只对provider字段等于当前配置 provider 名称的媒体生效其它 provider 写入的历史媒体不会被签名getSignedUrl只在isPrivate()返回true时才被调用大小校验的sizeLimit是 upload 配置的顶层参数从providerOptions传入属于已弃用方式local provider 会发出弃用告警提示迁移到upload.config。参考文件Provider 契约文档docs/docs/docs/01-core/upload/01-backend/00-providers.mdProvider 加载与启动校验packages/core/upload/server/src/register.ts签名 URL 流程packages/core/upload/server/src/services/file.ts官方 S3 provider 实现packages/providers/upload-aws-s3/src/index.ts官方 local provider 实现packages/providers/upload-local/src/index.ts【免费下载链接】strapi Strapi is the leading open-source headless CMS. It’s 100% JavaScript/TypeScript, fully customizable, and developer-first.项目地址: https://gitcode.com/GitHub_Trending/st/strapi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考