Настройка медиа-библиотеки Strapi
Медиа-библиотека Strapi по умолчанию хранит файлы локально в public/uploads. Для production это неприемлемо: файлы не реплицируются между инстансами, не масштабируются, теряются при пересборке контейнера. Настройка включает перенос хранилища на S3-совместимое облако и конфигурацию брейкпоинтов изображений.
Провайдеры хранилища
| Провайдер | Пакет |
|---|---|
| AWS S3 | @strapi/provider-upload-aws-s3 |
| Cloudinary | @strapi/provider-upload-cloudinary |
| DigitalOcean Spaces | через aws-s3 (S3-совместимый) |
| Cloudflare R2 | через aws-s3 + кастомный endpoint |
Настройка AWS S3
npm install @strapi/provider-upload-aws-s3
// config/plugins.js
module.exports = ({ env }) => ({
upload: {
config: {
provider: 'aws-s3',
providerOptions: {
s3Options: {
credentials: {
accessKeyId: env('AWS_ACCESS_KEY_ID'),
secretAccessKey: env('AWS_ACCESS_KEY_SECRET'),
},
region: env('AWS_REGION', 'eu-central-1'),
endpoint: env('AWS_ENDPOINT', undefined), // для S3-совместимых
params: {
ACL: env('AWS_ACL', 'public-read'),
Bucket: env('AWS_BUCKET'),
},
},
},
actionOptions: {
upload: {},
uploadStream: {},
delete: {},
},
},
},
});
Cloudflare R2
R2 совместим с S3 API, бесплатная egress полоса:
module.exports = ({ env }) => ({
upload: {
config: {
provider: 'aws-s3',
providerOptions: {
s3Options: {
credentials: {
accessKeyId: env('R2_ACCESS_KEY_ID'),
secretAccessKey: env('R2_SECRET_ACCESS_KEY'),
},
region: 'auto',
endpoint: `https://${env('CF_ACCOUNT_ID')}.r2.cloudflarestorage.com`,
params: {
Bucket: env('R2_BUCKET'),
},
},
},
},
},
});
Для публичного доступа к файлам нужен кастомный домен R2 или Cloudflare Worker.
Cloudinary
Автоматическая оптимизация и трансформации:
npm install @strapi/provider-upload-cloudinary
module.exports = ({ env }) => ({
upload: {
config: {
provider: 'cloudinary',
providerOptions: {
cloud_name: env('CLOUDINARY_NAME'),
api_key: env('CLOUDINARY_KEY'),
api_secret: env('CLOUDINARY_SECRET'),
},
actionOptions: {
upload: {
folder: env('CLOUDINARY_FOLDER', 'strapi'),
transformation: [{ quality: 'auto', fetch_format: 'auto' }],
},
uploadStream: {},
delete: {},
},
},
},
});
Форматы изображений и брейкпоинты
Strapi автоматически создаёт несколько размеров при загрузке:
// config/plugins.js — в секции upload.config
breakpoints: {
xlarge: 1920,
large: 1000,
medium: 750,
small: 500,
xsmall: 64,
},
Форматы хранятся в formats поле медиа-объекта. В API-ответе:
{
"url": "/uploads/image.jpg",
"formats": {
"thumbnail": { "url": "/uploads/thumbnail_image.jpg", "width": 156 },
"small": { "url": "/uploads/small_image.jpg", "width": 500 },
"medium": { "url": "/uploads/medium_image.jpg", "width": 750 }
}
}
Ограничение размера и типов файлов
// config/plugins.js
module.exports = {
upload: {
config: {
sizeLimit: 20 * 1024 * 1024, // 20 МБ
// Фильтрация по MIME через middleware или lifecycle hook
},
},
};
Фильтрация типов через lifecycle:
// src/extensions/upload/strapi-server.js
module.exports = (plugin) => {
const originalUpload = plugin.controllers.upload.upload;
plugin.controllers.upload.upload = async (ctx) => {
const { files } = ctx.request;
const allowed = ['image/jpeg', 'image/png', 'image/webp', 'application/pdf'];
const invalid = Object.values(files).flat()
.filter(f => !allowed.includes(f.type));
if (invalid.length) {
return ctx.badRequest('Неподдерживаемый тип файла');
}
return originalUpload(ctx);
};
return plugin;
};
Сроки
- Настройка S3/R2/Cloudinary провайдера — несколько часов
- Перенос существующих файлов с локального хранилища — 1 день (зависит от объёма)
- Кастомная фильтрация типов + ограничения — несколько часов







