Skip to main content

亚马逊 S3 提供商

🌐 Amazon S3 provider

Page summary:

@strapi/provider-upload-aws-s3 包允许你将媒体库资源存储在 Amazon S3 或任何兼容 S3 的服务上(如 Cloudflare R2、Scaleway、MinIO 等)。本页面涵盖了提供商配置、必要的 AWS 设置(IAM、CORS、中间件)以及扩展选项,如加密、校验和以及用于私有存储桶的签名 URL。

🌐 The @strapi/provider-upload-aws-s3 package lets you store Media Library assets on Amazon S3 or any S3-compatible service (Cloudflare R2, Scaleway, MinIO, etc.). This page covers provider configuration, required AWS setup (IAM, CORS, middleware), and extended options such as encryption, checksums, and signed URLs for private buckets.

媒体库 功能由一个名为 Upload 的后端服务器包提供支持,该包利用了提供程序的使用。

🌐 The Media Library feature is powered by a back-end server package called Upload which leverages the use of providers.

Strapi 为媒体库维护了 3 个提供者。本页面关于 Amazon S3 提供者的安装和配置。有关其他提供者,请参阅 媒体库页面 中的列表。

🌐 Strapi maintains 3 providers for the Media Library. The present page is about the Amazon S3 provider installation and configuration. For other providers, please refer to the list in the Media Library page.

安装

🌐 Installation

要安装官方 Strapi 维护的 AWS S3 提供程序,请在终端中运行以下命令:

🌐 To install the official Strapi-maintained AWS S3 provider, run the following command in a terminal:

yarn add @strapi/provider-upload-aws-s3

配置

🌐 Configuration

提供程序配置在 “/config/plugins” 文件 中定义。如果该文件不存在,请先创建它。使用每个环境不同的提供程序时,请在 /config/env/${yourEnvironment}/plugins.js|ts 中指定正确的配置(参见 环境)。

🌐 Providers configuration is defined in the /config/plugins file. If this file does not exist, create it first. When using a different provider per environment, specify the correct configuration in /config/env/${yourEnvironment}/plugins.js|ts (see environments).

提供者配置接受以下条目:

🌐 The provider configuration accepts the following entries:

  • provider 用于定义提供者名称(即 amazon-s3
  • providerOptions 用于定义在提供者构建过程中传递的选项(完整的选项列表请参见 AWS documentation ;在专门的扩展提供者选项部分中给出了一些示例)
  • actionOptions 用于定义直接传递给每个方法各自参数的选项。官方 AWS 文档列出了 upload/uploadStreamdelete 可用的选项。

基本示例

🌐 Basic example

/config/plugins.js
module.exports = ({ env }) => ({
// ...
upload: {
config: {
provider: 'aws-s3',
providerOptions: {
baseUrl: env('CDN_URL'),
rootPath: env('CDN_ROOT_PATH'),
s3Options: {
credentials: {
accessKeyId: env('AWS_ACCESS_KEY_ID'),
secretAccessKey: env('AWS_ACCESS_SECRET'),
},
region: env('AWS_REGION'),
params: {
ACL: env('AWS_ACL', 'public-read'),
signedUrlExpires: env('AWS_SIGNED_URL_EXPIRES', 15 * 60),
Bucket: env('AWS_BUCKET'),
},
},
},
actionOptions: {
upload: {},
uploadStream: {},
delete: {},
},
},
},
// ...
});

如果你将存储桶用作 CDN 并在自定义域上交付内容,你可以使用 baseUrlrootPath 属性。使用环境配置来定义你的资源 URL 将如何保存在 Strapi 中。

🌐 If you use the bucket as a CDN and deliver the content on a custom domain, you can use the baseUrl and rootPath properties. Use environment configurations to define how your asset URLs will be saved inside Strapi.

AWS SDK V3 URL format

该提供者使用 AWS SDK V3,它默认对 S3 URL 使用 virtual-hosted-style URIs 。如果要改为使用路径式 URL,请显式设置 baseUrl

baseUrl: `https://s3.${process.env.AWS_REGION}.amazonaws.com/${process.env.AWS_BUCKET}`,
Caution

为了确保提供者正常工作,你还需要配置 IAM 权限、存储桶 CORS 以及 Strapi 安全中间件(参见 必要设置)。

🌐 To ensure the provider works correctly, you also need to configure IAM permissions, bucket CORS, and the Strapi security middleware (see Required setup).

私有存储桶和签名 URL

🌐 Private bucket and signed URLs

如果你的存储桶配置为私有,请在 params 对象中将 ACL 选项设置为 private。这可以确保文件的 URL 是签名的。

🌐 If your bucket is configured to be private, set the ACL option to private in the params object. This ensures file URLs are signed.

你可以通过在 params 对象中设置 signedUrlExpires 选项来定义签名 URL 的过期时间。默认值是 15 分钟。

🌐 You can define the expiration time of the signed URL by setting the signedUrlExpires option in the params object. The default value is 15 minutes.

Note

如果你正在使用 CDN,URL 将不会被签名。

🌐 If you are using a CDN, the URLs will not be signed.

/config/plugins.js
module.exports = ({ env }) => ({
// ...
upload: {
config: {
provider: 'aws-s3',
providerOptions: {
s3Options: {
credentials: {
accessKeyId: env('AWS_ACCESS_KEY_ID'),
secretAccessKey: env('AWS_ACCESS_SECRET'),
},
region: env('AWS_REGION'),
params: {
ACL: 'private', // <== set ACL to private
signedUrlExpires: env('AWS_SIGNED_URL_EXPIRES', 15 * 60),
Bucket: env('AWS_BUCKET'),
},
},
},
actionOptions: {
upload: {},
uploadStream: {},
delete: {},
},
},
},
// ...
});

所需设置

🌐 Required setup

以下是为了让你的 AWS S3 设置能够与媒体库配合使用,需要考虑的最低配置操作。

🌐 The following are the minimum configuration actions to take into account for your AWS S3 setup to work with the Media Library.

IAM 策略操作

🌐 IAM policy actions

以下是 AWS S3 提供程序工作所需的最低权限:

🌐 The following are the minimum amount of permissions needed for the AWS S3 provider to work:

"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:ListBucket",
"s3:DeleteObject",
"s3:PutObjectAcl"
],

存储桶 CORS 配置

🌐 Bucket CORS configuration

要显示上传到 S3 的 GIF 和视频的缩略图,请编辑你的存储桶的 CORS 配置,以便在 Strapi 中正确显示缩略图。操作方法如下:

🌐 To display thumbnails for GIFs and videos uploaded to S3, edit your bucket's CORS configuration so that thumbnails are properly shown in Strapi. To do so:

  1. 在 AWS 控制台中打开你的存储桶。
  2. 导航到 权限 选项卡。
  3. 找到 跨域资源共享 (CORS) 字段。
  4. 添加以下 CORS 策略(或根据你的需要进行调整):
[
{
"AllowedHeaders": ["*"],
"AllowedMethods": ["GET"],
"AllowedOrigins": ["YOUR STRAPI URL"],
"ExposeHeaders": [],
"MaxAgeSeconds": 3000
}
]

安全中间件配置

🌐 Security middleware configuration

默认的 Strapi 安全中间件设置会阻止媒体库中的 S3 缩略图预览。修改 contentSecurityPolicy 设置以允许从你的 S3 存储桶加载媒体。将 strapi::security 字符串替换为以下对象(详情请参见 中间件配置):

🌐 The default Strapi security middleware settings block S3 thumbnail previews in the Media Library. Modify the contentSecurityPolicy settings to allow loading media from your S3 bucket. Replace the strapi::security string with the following object (see middleware configuration for details):

/config/middlewares.js
module.exports = [
// ...
{
name: 'strapi::security',
config: {
contentSecurityPolicy: {
useDefaults: true,
directives: {
'connect-src': ["'self'", 'https:'],
'img-src': [
"'self'",
'data:',
'blob:',
'market-assets.strapi.io',
'yourBucketName.s3.yourRegion.amazonaws.com',
],
'media-src': [
"'self'",
'data:',
'blob:',
'market-assets.strapi.io',
'yourBucketName.s3.yourRegion.amazonaws.com',
],
upgradeInsecureRequests: null,
},
},
},
},
// ...
];

如果你的存储桶名称包含点,并且 forcePathStylefalse,S3 会使用目录式 URL,例如在:

🌐 If your bucket name contains dots and forcePathStyle is false, S3 uses directory-style URLs, such as in:

s3.yourRegion.amazonaws.com/your.bucket.name/image.jpg

在这种情况下,对于 img-srcmedia-src 指令,请使用 s3.yourRegion.amazonaws.com(不带桶名称)。

🌐 In this case, use s3.yourRegion.amazonaws.com (without the bucket name) for the img-src and media-src directives.

与S3兼容的服务

🌐 S3-compatible services

AWS S3 提供程序通过使用 endpoint 选项与兼容 S3 的服务一起工作。对于返回不正确 Location 格式的分块上传的兼容 S3 服务(例如 IONOS、MinIO),该提供程序会自动构建正确的 URL。

🌐 The AWS S3 provider works with S3-compatible services by using the endpoint option. The provider automatically constructs correct URLs for S3-compatible services that return incorrect Location formats for multipart uploads (e.g., IONOS, MinIO).

有些提供商在 s3Options 中要求 forcePathStyle: true。当提供商不支持虚拟主机样式的 URL(例如 bucket.endpoint.com)而是使用路径样式的 URL(例如 endpoint.com/bucket)时,需要此选项。

🌐 Some providers require forcePathStyle: true in the s3Options. This option is needed when the provider does not support virtual-hosted-style URLs (e.g., bucket.endpoint.com), and instead uses path-style URLs (e.g., endpoint.com/bucket).

下表显示了每个提供者的兼容性设置:

🌐 The following table shows compatibility settings for each provider: | 提供商 | forcePathStyle | ACL | 备注 || --- | --- | --- | --- || IONOS | true | 支持 | 多部分 Location 错误自动修复 || MinIO | true | 支持 | - || Contabo | true | 支持 | - || Hetzner | true | 支持 | - || DigitalOcean Spaces | 不需要 | 支持 | - || Wasabi | 不需要 | 支持 | - || Scaleway | 不需要 | 支持 | - || Vultr | 不需要 | 支持 | - || Backblaze B2 | 不需要 | 支持 | - || Cloudflare R2 | 不需要 | 不支持 | 从参数中省略 ACL |

Scaleway

/config/plugins.js
module.exports = ({ env }) => ({
// ...
upload: {
config: {
provider: 'aws-s3',
providerOptions: {
s3Options: {
credentials: {
accessKeyId: env('SCALEWAY_ACCESS_KEY_ID'),
secretAccessKey: env('SCALEWAY_ACCESS_SECRET'),
},
region: env('SCALEWAY_REGION'), // e.g "fr-par"
endpoint: env('SCALEWAY_ENDPOINT'), // e.g. "https://s3.fr-par.scw.cloud"
params: {
Bucket: env('SCALEWAY_BUCKET'),
},
},
},
},
},
// ...
});

IONOS / MinIO / Contabo

这些提供商需要 forcePathStyle: true,因为它们使用路径风格的 URL 而不是虚拟主机风格的 URL。

🌐 These providers require forcePathStyle: true because they use path-style URLs instead of virtual-hosted-style URLs.

/config/plugins.js
module.exports = ({ env }) => ({
upload: {
config: {
provider: 'aws-s3',
providerOptions: {
s3Options: {
credentials: {
accessKeyId: env('S3_ACCESS_KEY_ID'),
secretAccessKey: env('S3_ACCESS_SECRET'),
},
region: env('S3_REGION'),
endpoint: env('S3_ENDPOINT'),
forcePathStyle: true, // Required for these providers
params: {
Bucket: env('S3_BUCKET'),
},
},
},
},
},
});

Cloudflare R2

Cloudflare R2 不支持 ACL。

🌐 Cloudflare R2 does not support ACLs.

/config/plugins.js
module.exports = ({ env }) => ({
upload: {
config: {
provider: 'aws-s3',
providerOptions: {
s3Options: {
credentials: {
accessKeyId: env('R2_ACCESS_KEY_ID'),
secretAccessKey: env('R2_ACCESS_SECRET'),
},
region: 'auto',
endpoint: env('R2_ENDPOINT'), // e.g. "https://<account-id>.r2.cloudflarestorage.com"
params: {
Bucket: env('R2_BUCKET'),
// Do NOT set ACL - R2 does not support ACLs
},
},
},
},
},
});

扩展提供者选项

🌐 Extended provider options

providerOptions 中的 providerConfig 选项提供了用于数据完整性、安全性和成本优化的额外功能。

🌐 The providerConfig option inside providerOptions provides additional features for data integrity, security, and cost optimization.

生产环境安全最佳实践

校验和验证

🌐 Checksum validation

启用自动校验和计算以确保上传期间的数据完整性。SDK 在客户端计算校验和,S3 在服务器端验证校验和。

🌐 Enable automatic checksum calculation to ensure data integrity during uploads. The SDK calculates a checksum on the client side, and S3 validates the checksum server-side.

providerOptions: {
s3Options: { /* ... */ },
providerConfig: {
checksumAlgorithm: 'CRC64NVME', // Options: 'CRC32', 'CRC32C', 'SHA1', 'SHA256', 'CRC64NVME'
},
},

CRC64NVME 被推荐用于在现代硬件上获得最佳性能。

条件写入(防止覆盖)

🌐 Conditional writes (prevent overwrites)

通过启用条件写入来防止由于竞争条件导致的意外文件覆盖。启用后,如果已经存在具有相同键的对象,上传将会失败。

🌐 Prevent accidental file overwrites due to race conditions by enabling conditional writes. When enabled, uploads will fail if an object with the same key already exists.

providerConfig: {
preventOverwrite: true,
},

存储类型(仅限 AWS S3)

🌐 Storage class (AWS S3 only)

通过为上传的对象指定存储类别来优化存储成本。对于不经常访问的数据,请使用成本较低的存储类别。

🌐 Optimize storage costs by specifying a storage class for uploaded objects. Use lower-cost classes for infrequently accessed data.

Note

存储类是 AWS S3 特有的。其他兼容 S3 的提供商(MinIO、DigitalOcean Spaces、IONOS、Wasabi)将忽略此设置。

🌐 Storage classes are AWS S3-specific. Other S3-compatible providers (MinIO, DigitalOcean Spaces, IONOS, Wasabi) will ignore this setting.

providerConfig: {
storageClass: 'INTELLIGENT_TIERING', // Auto-optimizes costs
},

可用存储类:

🌐 Available storage classes:

  • STANDARD - 频繁访问的数据(默认)
  • INTELLIGENT_TIERING - 自动成本优化
  • STANDARD_IA - 不常访问的数据
  • ONEZONE_IA - 不常访问,单可用区
  • GLACIER - 档案存储
  • DEEP_ARCHIVE - 长期存档
  • GLACIER_IR - Glacier 即时检索

服务器端加密

🌐 Server-side encryption

为合规要求(GDPR、HIPAA 等)配置服务器端加密。

🌐 Configure server-side encryption for compliance requirements (GDPR, HIPAA, etc.).

providerConfig: {
encryption: {
type: 'AES256', // S3-managed encryption
},
},

对于 KMS 管理的加密(仅适用于 AWS S3):

🌐 For KMS-managed encryption (AWS S3 only):

providerConfig: {
encryption: {
type: 'aws:kms',
kmsKeyId: env('AWS_KMS_KEY_ID'),
},
},

可用的加密类型:

🌐 Available encryption types:

  • AES256 - S3 管理的密钥(SSE-S3)——大多数兼容 S3 的提供商支持
  • aws:kms - AWS KMS 管理的密钥(SSE-KMS)——仅限 AWS S3
  • aws:kms:dsse - 双层 SSE 与 KMS — 仅限 AWS S3

对象标记

🌐 Object tagging

对上传的对象应用标签,以进行成本分配、生命周期策略和组织管理。

🌐 Apply tags to uploaded objects for cost allocation, lifecycle policies, and organization.

providerConfig: {
tags: {
project: 'website',
environment: 'production',
team: 'backend',
},
},

多部分上传配置

🌐 Multipart upload configuration

配置大文件的分段上传行为。

🌐 Configure multipart upload behavior for large files.

providerConfig: {
multipart: {
partSize: 10 * 1024 * 1024, // 10MB per part
queueSize: 4, // Number of parallel uploads
leavePartsOnError: false, // Clean up on failure
},
},

完整配置示例

🌐 Complete configuration example

/config/plugins.js
module.exports = ({ env }) => ({
upload: {
config: {
provider: 'aws-s3',
providerOptions: {
baseUrl: env('CDN_URL'),
rootPath: env('CDN_ROOT_PATH'),
s3Options: {
credentials: {
accessKeyId: env('AWS_ACCESS_KEY_ID'),
secretAccessKey: env('AWS_ACCESS_SECRET'),
},
region: env('AWS_REGION'),
params: {
ACL: 'private',
signedUrlExpires: 15 * 60,
Bucket: env('AWS_BUCKET'),
},
},
providerConfig: {
checksumAlgorithm: 'CRC64NVME',
preventOverwrite: true,
storageClass: 'INTELLIGENT_TIERING',
encryption: {
type: 'aws:kms',
kmsKeyId: env('AWS_KMS_KEY_ID'),
},
tags: {
application: 'strapi',
environment: env('NODE_ENV'),
},
multipart: {
partSize: 10 * 1024 * 1024,
queueSize: 4,
},
},
},
},
},
});

自定义提供程序覆盖(私有 S3 提供程序)

🌐 Custom provider override (private S3 provider)

对于大多数私有存储桶的使用场景,在提供程序配置中设置 ACL: 'private'(参见 私有存储桶和签名 URL)就足够了。提供程序会自动处理 URL 签名。

🌐 For most private bucket use cases, setting ACL: 'private' in the provider configuration (see Private bucket and signed URLs) is sufficient. The provider handles URL signing automatically.

但是,如果你需要对签名逻辑进行完全控制,你可以在本地重写提供程序。这可以用于为每个文件设置自定义过期时间、条件访问规则,或与外部授权服务的集成。

🌐 However, if you need full control over the signing logic, you can override the provider locally. This could be used to set custom expiration times per file, conditional access rules, or integration with an external authorization service.

要创建自定义 aws-s3 提供程序覆盖:

🌐 To create a custom aws-s3 provider override:

  1. 在你的应用中创建一个 /providers/aws-s3 文件夹(更多信息请参见 本地提供者)。
  2. aws-s3 提供程序中实现 isPrivate() 方法以返回 true
  3. aws-s3 提供者中实现 getSignedUrl(file) 方法,以为给定的文件生成签名 URL。
/providers/aws-s3/index.js
module.exports = {
init: (config) => {
const s3 = new AWS.S3(config);

return {
async upload(file) {
// code to upload file to S3
},

async delete(file) {
// code to delete file from S3
},

async isPrivate() {
return true;
},

async getSignedUrl(file) {
const params = {
Bucket: config.params.Bucket,
Key: file.path,
Expires: 60, // URL expiration time in seconds
};

const signedUrl = await s3.getSignedUrlPromise("getObject", params);
return { url: signedUrl };
},
};
},
};