Розробка системи медіа-бібліотеки для CMS сайту

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.
Розробка та обслуговування будь-яких видів сайтів:
Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Розробка системи медіа-бібліотеки для CMS сайту
Середня
від 1 тижня до 3 місяців
Часті питання
Наші компетенції:
Етапи розробки
Останні роботи
  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Розробка системи медіа-бібліотеки для CMS

Медіа-бібліотека — централізоване сховище та інтерфейс управління файлами: зображення, відео, документи. Замість завантаження одного файлу для кожного запису, редактор вибирає з уже завантажених файлів — менше дублів, менше витрат на зберігання.

Модель даних

media_files (
  id, original_name, file_name, disk, path,
  mime_type, size,
  width, height,        -- для зображень
  alt, title, caption,  -- SEO та доступність
  folder_id,
  uploaded_by,
  created_at, updated_at
)

media_folders (
  id, name, parent_id, created_at
)

media_conversions (
  id, media_id, name,  -- 'thumb', 'medium', 'large'
  file_name, width, height, size, created_at
)

Завантаження файлів через Presigned URL

Завантаження безпосередньо з браузера на S3 — без навантаження на сервер:

// Сервер генерує presigned URL
$s3 = new S3Client([...]);
$cmd = $s3->getCommand('PutObject', [
    'Bucket'      => env('AWS_BUCKET'),
    'Key'         => "uploads/temp/{$uuid}/{$filename}",
    'ContentType' => $mimeType
]);
$presigned = $s3->createPresignedRequest($cmd, '+15 minutes');

return ['upload_url' => (string)$presigned->getUri(), 'key' => $key];
// Клієнт завантажує безпосередньо на S3
const { upload_url, key } = await getPresignedUrl(file);
await fetch(upload_url, { method: 'PUT', body: file, headers: { 'Content-Type': file.type } });

// Підтвердити завантаження серверу
await confirmUpload(key, { alt: '', title: file.name });

Генерація превью

Після завантаження — асинхронна генерація версій через чергу:

class GenerateImageConversions implements ShouldQueue
{
    public function handle(): void
    {
        $conversions = [
            'thumb'  => ['width' => 150,  'height' => 150],
            'medium' => ['width' => 800,  'height' => null],
            'large'  => ['width' => 1920, 'height' => null],
            'webp'   => ['width' => null, 'height' => null, 'format' => 'webp']
        ];

        foreach ($conversions as $name => $params) {
            $image = Image::make(Storage::get($this->media->path));
            if ($params['width']) {
                $image->resize($params['width'], $params['height'], fn($c) => $c->aspectRatio());
            }
            if (isset($params['format'])) {
                $image->encode($params['format'], 85);
            }
            Storage::put("media/conversions/{$this->media->id}/{$name}", $image->stream());
        }
    }
}

Інтерфейс медіа-бібліотеки

Основні можливості UI:

  • Сітка файлів з превью (або список)
  • Фільтри: тип файлу, папка, дата
  • Пошук по імені та alt-тексту
  • Завантаження drag-and-drop або кнопкою
  • Створення папок
  • Множинний виділення для масових операцій
  • Редагування alt, title, caption прямо в бібліотеці
  • Копіювання URL у буфер обміну

Віджет вибору медіа для редактора

function MediaPickerButton({ onSelect, multiple = false }) {
    const [isOpen, setIsOpen] = useState(false);

    return (
        <>
            <Button onClick={() => setIsOpen(true)}>Вибрати з бібліотеки</Button>
            <Dialog open={isOpen} onOpenChange={setIsOpen}>
                <DialogContent className="max-w-4xl h-[80vh]">
                    <MediaLibraryGrid
                        onSelect={(files) => {
                            onSelect(multiple ? files : files[0]);
                            setIsOpen(false);
                        }}
                        multiple={multiple}
                    />
                </DialogContent>
            </Dialog>
        </>
    );
}

Очистка невиконаних файлів

// Задача за розкладом: знайти файли без посилань
$unused = MediaFile::whereDoesntHave('usages')
    ->where('created_at', '<', now()->subDays(30))
    ->get();

foreach ($unused as $file) {
    Storage::delete($file->path);
    $file->delete();
}

Термін розробки: 3–4 тижні для повної медіа-бібліотеки з генерацією превью, папками та віджетом вибору.