We have recently migrated the content management of the Inmagik website to Payload CMS. To optimize media content management for the website, we use DigitalOcean Spaces, an S3-compatible cloud storage service that also features a CDN to optimize content serving and reduce webserver load.
Upload with Payload CMS: basic configuration.
Payload CMS has specific functionalities related to file uploads, which it manages through the identification of one or more data collections dedicated to media storage, which can be referenced by other collections to implement file uploads.
In practice, this upload model makes available a "media gallery" to which you can add images or other types of media and reuse them in various data models of our website.
The configuration is very simple: it's sufficient to create one or more collections for uploads, with a basic configuration like this:
import type { CollectionConfig } from 'payload'
export const Media: CollectionConfig = {
slug: 'media',
upload: {
staticDir: 'media',
imageSizes: [
{
name: 'thumbnail',
width: 400,
height: 300,
position: 'centre',
},
],
adminThumbnail: 'thumbnail',
mimeTypes: ['image/*'],
},
fields: [
{
name: 'alt',
type: 'text',
},
],
access: {
read: () => true,
}
}
and use this collection as an upload field. In our case, we added an upload field to our collection to manage our blog posts:
import type { CollectionConfig } from 'payload'
export const Blog: CollectionConfig = {
slug: 'blog',
admin: {
useAsTitle: 'slug',
},
fields: [
{
name: 'slug',
type: 'text',
required: true,
unique: true,
},
{
name: 'title',
type: 'text',
required: true,
localized: true,
},
// more fields here....
{
name: 'article',
type: 'code',
localized: true,
admin: {
language: 'markdown',
}
},
{
name: 'image',
type: 'upload',
relationTo: 'media',
required: false,
},
],
}
In our case, the media
collection is referenced by the blog
collection through the image
field of upload type.
The upload collection configured this way is however filesystem-based: added files will be saved on the server disk, in the location specified in the media
collection configuration.
Note that the configuration allows read access through the specification of the access
object, which in this case defines that reading is always possible.
Payload CMS Storage Adapters
To optimize media management and abstract the storage concept, Payload provides Storage Adapters. These adapters allow you to store files in different locations, such as Amazon S3, Vercel Blob Storage, Google Cloud Storage and others.
Configuring S3 storage for DigitalOcean Spaces
For our website we will use the S3 Storage adapter and configure it to use DigitalOcean Spaces, a file storage service compatible with Amazon S3.
It's therefore necessary to install the storage:
npm i @payloadcms/storage-s3
and configure Payload to use this library. The configuration is in the payload.config.ts
file. We only report the parts of the configuration that concern the storage adapter.
import path from 'path'
import { buildConfig } from 'payload'
import { s3Storage } from '@payloadcms/storage-s3'
import { Media } from './collections/Media'
import { Blog } from './collections/Blog'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
export default buildConfig({
// more configuration keys here ...
// Define and configure your collections in this array
collections: [Media, Blog],
typescript: {
outputFile: path.resolve(dirname, 'payload-types.ts'),
},
plugins: [
s3Storage({
collections: {
media: {
generateFileURL: ({ filename }) => {
return `https://${process.env.S3_BUCKET}.${process.env.S3_REGION}.cdn.digitaloceanspaces.com/${filename}`
},
},
},
acl: 'public-read',
bucket: process.env.S3_BUCKET || '',
config: {
credentials: {
accessKeyId: process.env.S3_ACCESS_KEY_ID || '',
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY || '',
},
// ... Other S3 configuration
region: process.env.S3_REGION || 'ams3',
endpoint: process.env.S3_ENDPOINT || undefined,
},
}),
// more plugins configured here
],
})
This Payload configuration enables the S3 storage plugin for the media
collection defined above.
The important parts of this configuration are:
- the definition of a function to generate a file URL, which builds the URL based on our CDN. (In this case we are only handling the URL for the "base" image and not for the various sizes configured in the upload collection).
- The
acl
policy that uploads files aspublic-read
, making them accessible to anyone once the URL is known. Obviously, for proper functioning, the constantsS3_BUCKET, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, S3_ENDPOINT, S3_REGION
must be defined (typically in a.env
file).