Налаштування медіа-бібліотеки 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 день (залежить від обсягу)
- Кастомна фільтрація типів + обмеження — кілька годин







