文件上传

Payload 提供了直接在服务器上启用文件上传、存储和管理所需的一切功能——包括极其强大的文件访问控制

展示 Payload 管理面板中启用了上传功能的集合
管理面板截图,展示启用了上传功能的媒体集合

以下是上传功能的一些常见用例:

  • 创建包含网站或应用所需图片的"媒体库"
  • 构建需要用户注册才能访问的付费内容库,如电子书 PDF、白皮书等可下载资源
  • 存储公开可下载的资源,如软件、ZIP 文件、MP4 等

只需在集合上启用上传功能,Payload 就会自动将你的集合转变为强大的文件管理/存储解决方案。系统会进行以下修改:

  1. 会自动向你的集合添加 filenamemimeTypefilesize 字段。如果你在集合的上传配置中传递了 imageSizes,还会添加包含自动调整大小的图片尺寸和文件名的 sizes 数组。
  2. 管理面板会修改其内置的 List 组件,在列表视图中显示每个上传文件的缩略图
  3. 管理面板会修改其 Edit 视图,添加一组对应的上传 UI,允许文件上传
  4. 修改 createupdatedelete 集合操作以支持文件上传、重新上传和删除

启用上传功能

每个 Payload Collection 都可以通过在其配置中指定 upload 属性来支持上传功能,该属性可以设置为 true 或包含上传选项的对象。

提示:

常见做法是创建一个名为 "media" 的集合并为该集合启用 upload 功能。

import type { CollectionConfig } from 'payload'

export const Media: CollectionConfig = {
  slug: 'media',
  upload: {
    staticDir: 'media',
    imageSizes: [
      {
        name: 'thumbnail',
        width: 400,
        height: 300,
        position: 'centre',
      },
      {
        name: 'card',
        width: 768,
        height: 1024,
        position: 'centre',
      },
      {
        name: 'tablet',
        width: 1024,
        // 通过指定 `undefined` 或保留高度未定义,
        // 图片将按指定宽度调整大小,
        // 但会保持原始宽高比
        // 并自动计算高度。
        height: undefined,
        position: 'centre',
      },
    ],
    adminThumbnail: 'thumbnail',
    mimeTypes: ['image/*'],
  },
  fields: [
    {
      name: 'alt',
      type: 'text',
    },
  ],
}

Collection 上传选项

带星号(*)的选项表示必填项。

OptionDescription
adminThumbnail设置 Admin Panel 如何显示该 Collection 的缩略图。了解更多
bulkUpload允许用户在列表视图中批量上传,默认为 true
cacheTags设置为 false 可禁用 admin thumbnail 组件在 UI 中设置的缓存标签。当 CDN 不允许某些缓存查询时很有用。
crop设置为 false 可禁用 Admin Panel 中的裁剪工具。裁剪功能默认启用。了解更多
disableLocalStorage完全禁用将文件上传到本地磁盘。了解更多
displayPreview启用预览与此 Collection 相关的 Upload 字段中上传的文件。可在 Upload 字段中通过 displayPreview 选项局部覆盖。了解更多
externalFileHeaderFilter接收现有 headers 并返回过滤或修改后的 headers。
filesRequiredOnCreate创建时强制要求文件数据,默认为 true。
filenameCompoundIndex用于复合索引的字段 slugs,替代默认的文件名索引。
focalPoint设置为 false 可禁用 Admin Panel 中的焦点选择工具。焦点选择器仅在定义了 imageSizesresizeOptions 时可用。了解更多
formatOptions包含 formatoptions 的对象,用于与 Sharp 图像库一起格式化上传文件。了解更多
handlers获取文件时执行的 Request handlers 数组,如果 handler 返回 Response,它将发送给客户端。否则 Payload 将检索并返回文件。
imageSizes如果指定,图像上传将根据这些图像尺寸自动调整大小。了解更多
mimeTypes限制文件选择器中的 mimeTypes。有效的 mimetype 数组或 mimetype 通配符。了解更多
pasteURL控制是否可以通过将远程 URL 粘贴到 Upload 字段来上传文件。默认启用。接受 false 禁用或带有有效远程 URL allowList 的对象。了解更多
resizeOptions传递给 Sharp 图像库以调整上传文件大小的对象。了解更多
staticDir用于存储媒体文件的文件夹目录。可以是绝对路径或相对于包含配置的目录的路径。默认为你的 collection slug
trimOptions传递给 Sharp 图像库以修剪上传文件的对象。了解更多
withMetadata如果指定,将元数据附加到输出图像文件。接受布尔值或接收 metadatareq 并返回布尔值的函数。
hideFileInputOnCreate设置为 true 可防止 admin UI 在创建文档时显示文件输入,对程序化文件生成很有用。
hideRemoveFile设置为 true 可防止 admin UI 在编辑时有移除现有文件的方式。

全局上传选项

上传选项可以针对每个 Collection 单独配置,你也可以通过向基础 Payload Config 传递一个包含所有 Busboy 配置选项的 upload 属性对象来控制应用范围内的上传设置。

选项描述
abortOnLimit布尔值,如果设为 true,当文件超过大小限制时会返回 HTTP 413 错误。如果设为 false,文件会被截断。默认为 false
createParentPath设为 true 时,在从临时目录或缓冲区移动文件时会自动创建目录路径。默认为 false
debug布尔值,设为 true 时开启上传过程日志记录,false 时关闭。用于故障排查。默认为 false
limitHandler当文件超过配置限制时调用的函数。
parseNested设为 true 时,将 req.bodyreq.files 转换为嵌套结构。默认情况下它们是扁平对象。默认为 false
preserveExtensionsafeFileNames 选项配合使用时保留文件扩展名。如果设为 true 则限制扩展名为 3 个字符,如果设为 number 则自定义长度,从扩展名开头开始截断。
responseOnLimit当文件大小超过限制时(与 abortOnLimit 配合使用)发送给客户端的响应字符串。
safeFileNames设为 true 时去除除破折号和下划线外的非字母数字字符。也可以设置为正则表达式来自定义去除规则。默认为 false
tempFileDiruseTempFiles 设为 true 时,用于存储临时文件的路径字符串。默认为 './tmp'
uploadTimeout定义在终止上传前等待数据的毫秒数。设为 0 则禁用超时检查。默认为 60000
uriDecodeFileNames设为 true 时对文件名进行 URI 解码。默认为 false
useTempFiles设为 true 时将文件存储到临时目录而非内存中,可减少大文件或多文件上传时的内存占用。

点击这里查看关于 Busboy 可配置选项的更多文档。

一个常见的全局上传选项自定义场景是增加 Payload 允许上传的 fileSize

import { buildConfig } from 'payload'

export default buildConfig({
  collections: [
    {
      slug: 'media',
      fields: [
        {
          name: 'alt',
          type: 'text',
        },
      ],
      upload: true,
    },
  ],
  upload: {
    limits: {
      fileSize: 5000000, // 5MB,以字节为单位
    },
  },
})

通过 hooks 自定义文件名

你可以使用 beforeOperation hook 在文件上传到服务器之前自定义文件名。

beforeOperation: [
  ({ req, operation }) => {
    if ((operation === 'create' || operation === 'update') && req.file) {
      req.file.name = 'test.jpg'
    }
  },
],

req.file 对象将包含文件的额外信息,如 mimeType 和扩展名,同时你也可以完全访问文件数据本身。 从这里设置的文件名也会传递给启用的图片尺寸配置。

图片尺寸

如果在 upload 配置中指定了 imageSizes 数组,Payload 会自动裁剪和调整上传图片的大小以匹配配置中指定的每个尺寸。

Admin Panel 也会自动显示所有可用的文件,包括每个上传图片的宽度、高度和文件大小。

在底层实现上,Payload 依赖 sharp 来执行图片尺寸调整。你可以为 sharp 指定额外的选项来调整图片。

请注意,要使图片尺寸调整功能正常工作,必须在 Payload Config 中指定 sharp。如果你使用 create-payload-app 创建 Payload 项目,默认已配置此项。详见 Config Options 中的 sharp 部分。

在 hooks 中访问调整后的图片

所有自动调整大小的图片都会通过绑定到 req.payloadUploadSizes 的对象暴露出来,以便在 hooks 等场景中重复使用。

该对象的键将是生成的每个尺寸名称,每个键对应的值将是包含文件数据的 buffer。

处理图像放大

当上传的图像小于定义的尺寸时,我们有3个选项:

withoutEnlargement: undefined | false | true

  1. undefined [默认]:当上传图像的宽度和高度都小于目标尺寸时返回 null
  2. false:始终将图像放大至目标尺寸
  3. true:如果图像小于目标尺寸,则返回原始图像

注意:

默认情况下,当上传图像小于定义尺寸时,该尺寸会返回 NULL。使用 withoutEnlargement 属性可修改此行为。

为每个尺寸自定义文件名

每个图像尺寸都支持 generateImageName 函数,可用于为调整大小的图像生成自定义文件名。 该函数接收原始文件名、调整尺寸名称、扩展名、高度和宽度作为参数。

{
  name: 'thumbnail',
  width: 400,
  height: 300,
  generateImageName: ({ height, sizeName, extension, width }) => {
    return `custom-${sizeName}-${height}-${width}.${extension}`
  },
}

裁剪与焦点选择器

此功能仅适用于图像文件类型。

在 Upload 配置中设置 crop: falsefocalPoint: false 将禁用 Admin Panel 中相应的选择器。

图像裁剪发生在任何调整大小操作之前,因此调整后的图像将基于裁剪后的图像生成(而非原始图像)。

如果未指定调整大小选项(imageSizesresizeOptions),则不会显示焦点选择器。

禁用本地上传存储

如果你正在使用插件将文件发送到第三方文件存储主机或 CDN(如 Amazon S3 等),你可能完全不想在本地存储文件。你可以在 collection 的上传配置中指定 disableLocalStorage: true 来阻止 Payload 将文件写入磁盘。

注意:

这是一个相当高级的功能。如果你禁用了本地文件存储,默认情况下,管理面板的缩略图将无法显示,因为你没有存储文件。你需要完全依靠插件或自定义钩子来永久存储文件,并使用 upload.adminThumbnail 提供自定义的管理缩略图。

管理缩略图

你可以通过以下方式之一指定 Payload 如何为支持上传的 Collections 获取管理缩略图:

  1. adminThumbnail 设为字符串,等于你提供的某个图片尺寸名称。
import type { CollectionConfig } from 'payload'

export const Media: CollectionConfig = {
  slug: 'media',
  upload: {
    // highlight-start
    adminThumbnail: 'small',
    // highlight-end
    imageSizes: [
      {
        name: 'small',
        fit: 'cover',
        height: 300,
        width: 900,
      },
      {
        name: 'large',
        fit: 'cover',
        height: 600,
        width: 1800,
      },
    ],
  },
}
  1. adminThumbnail 设为函数,该函数接收文档数据并返回加载缩略图的完整 URL。
import type { CollectionConfig } from 'payload'

export const Media: CollectionConfig = {
  slug: 'media',
  upload: {
    // highlight-start
    adminThumbnail: ({ doc }) =>
      `https://google.com/custom-path-to-file/${doc.filename}`,
    // highlight-end
  },
}

MimeTypes

通过指定 mimeTypes 属性可以限制用户从文件选择器中可选择哪些文件。该属性接受一个字符串数组,可以是任何有效的 MIME 类型或 MIME 类型通配符。

一些示例值包括:image/*audio/*video/*image/pngapplication/pdf

mimeTypes 使用示例:

import type { CollectionConfig } from 'payload'

export const Media: CollectionConfig = {
  slug: 'media',
  upload: {
    mimeTypes: ['image/*', 'application/pdf'], // highlight-line
  },
}

文件上传

重要提示:

由于 GraphQL 的工作机制,目前只能通过 REST 和 Local API 进行文件上传。通过 GraphQL 支持文件上传既困难又不太合理。

要上传文件,请使用你 collection 的 create 端点。发送你的 Collection 所需的所有数据,以及包含要上传文件的 file 键。

使用 FormDatamultipart/form-data 格式发送请求(如果可能)。

注意: 要包含任何附加字段(如 titlealt 等),需要添加一个 _payload 字段,其中包含所需值的 JSON 字符串化对象。这些值必须与支持上传的 collection 的 schema 匹配。

const fileInput = document.querySelector('#your-file-input')
const formData = new FormData()

formData.append('file', fileInput.files[0])

// 替换为你支持上传的 collection 中定义的字段
// 下面的示例包含了一个可选字段如 'title'
formData.append(
  '_payload',
  JSON.stringify({
    title: '示例标题',
    description: '文件的可选描述',
  }),
)

fetch('api/:upload-slug', {
  method: 'POST',
  body: formData,
  /**
   * 不要手动添加 Content-Type 请求头
   * 浏览器会自动处理
   *
   * headers: {
   *  'Content-Type': 'multipart/form-data'
   * }
   */
})

上传本地存储的文件

如果你想直接使用 payload.create 方法上传存储在本地机器上的文件(例如在种子脚本中),可以使用 filePath 属性指定文件的本地路径。

const localFilePath = path.resolve(__dirname, filename)

await payload.create({
  collection: 'media',
  data: {
    alt,
  },
  filePath: localFilePath,
})

data 属性仍需包含你的 media collection 中所有必填字段。

重要提示:

请记住,所有附加到 media collection 的自定义钩子仍会触发。 确保文件符合 collection 的 formatOptions 或自定义 hooks 中指定的 mimeTypes 或大小限制。

从远程 URL 上传文件

pasteURL 选项允许用户通过将远程 URL 粘贴到上传字段中来获取文件。此选项默认启用,可以配置为允许不受限制的客户端获取将服务器端获取限制为特定的受信任域

默认情况下,Payload 使用客户端获取,浏览器直接从提供的 URL 下载文件。然而,如果 URL 的服务器有 CORS 限制,客户端获取将会失败,因此仅适用于内部 URL 或没有 CORS 限制的公共 URL。

要从受限制的 URL(否则会被 CORS 阻止)获取文件,请通过配置带有受信任域 allowListpasteURL 选项来使用服务器端获取。此方法确保 Payload 在服务器上下载文件并将其流式传输到浏览器。但出于安全考虑,仅允许匹配指定 allowList 的 URL。

配置示例

以下是配置 pasteURL 选项来控制远程 URL 获取的方法:

import type { CollectionConfig } from 'payload'

export const Media: CollectionConfig = {
  slug: 'media',
  upload: {
    pasteURL: {
      allowList: [
        {
          hostname: 'payloadcms.com', // 必填
          pathname: '',
          port: '',
          protocol: 'https',
          search: ''
        },
        {
          hostname: 'example.com',
          pathname: '/images/*',
        },
      ],
    },
  },
}
pasteURL 可接受的值
选项描述
undefined默认行为。允许对内部或公共 URL 进行客户端获取。
false禁用向 Upload 字段粘贴 URL 的功能。
allowList允许对特定受信任 URL 进行服务端获取。需要提供一个定义受信任域的对象数组。有关 AllowItem 的详细信息请参见下表。
AllowItem 属性

带星号的选项表示必填项。

选项描述示例
hostname *允许 URL 的主机名。此项必填以确保 URL 来自可信来源。example.com
pathnameURL 的路径部分。支持通配符以匹配多个路径。/images/*
portURL 的端口号。如果未指定,将使用协议的默认端口。3000
protocol要匹配的协议。必须是 httphttps。默认为 httpshttps
searchURL 的查询字符串。如果指定,URL 必须完全匹配此查询字符串。?version=1

访问控制

上传到每个 Collection 的所有文件自动支持来自 Collection 本身的 read 访问控制 功能。你可以使用此功能来控制哪些用户应该被允许查看你的上传文件,哪些用户不应该被允许。