Увійти Реєстрація
Вакансії
Вакансії Laravel Компанії Статистика
Підготовка
Як підготуватися < href="https://laravelukraine.com/interviews" wire:navigate.hover class="flex items-center gap-3 rounded-md px-3 py-2 text-base font-medium text-gray-600 hover:bg-gray-50 hover:text-gray-900 dark:text-gray-400 dark:hover:bg-gray-900 dark:hover:text-white" >Співбесіди Тестування Документація
Блог
Всі статті Серії
Екосистема
Пакети Ресурси Проєкти Інструменти Події Відео
Інше
Про нас Реклама
Новини 14 листопада 2026

Laravel Storage Dedupler: один файл на диску замість десятка копій

Laravel Storage Dedupler Максима Хімусова зберігає завантажені файли без дублікатів: файл ідентифікується SHA-1 хешем свого вмісту, і скільки б разів його не завантажили, на диску лежить одна копія, а моделі отримують посилання на неї. Для застосунків, де користувачі вдесяте прикріплюють той самий договір, рахунок чи логотип, це пряма економія місця, часу бекапів і грошей на S3.

Пакет нещодавно оглянув Laravel News, а за кілька днів після огляду автор випустив версію 1.0.6, яка зробила налаштовуваною найспірнішу поведінку - видалення файлу після останнього відв'язування.

Як це працює

Усередині пакета дві таблиці. dedupler_unique_files зберігає по одному запису UniqueFile на кожен унікальний вміст, і первинний ключ цього запису - сам SHA-1 хеш. Друга - поліморфна проміжна таблиця, що пов'язує файл із будь-якими моделями та пам'ятає оригінальне ім'я файлу для кожного зв'язку.

Перед записом пакет рахує хеш і шукає UniqueFile з таким ключем. Знайшов - повертає наявний запис і за потреби лише додає зв'язок із моделлю. Не знайшов - кладе файл на диск і створює запис. Файли до 1 МБ читаються в пам'ять повністю, більші хешуються потоком шматками по 1 МБ. На диску файли розкладаються за першими символами хешу: da/39/da39a3ee....pdf.

Встановлення - дві команди. Друга створює міграції й перевіряє конфігурацію:

composer require maxkhim/laravel-storage-dedupler
php artisan dedupler:install

За замовчуванням файли лягають на окремий локальний диск deduplicated у storage/app/deduplicated. Диск змінюється через DEDUPLER_DISK, а таблиці можна винести в окреме з'єднання через DEDUPLER_CONNECTION.

Фасад Dedupler

Найпростіший спосіб - фасад. Три методи для трьох джерел: завантажений файл, файл на сервері за абсолютним шляхом і сирий вміст з іменем:

$uniqueFile = Dedupler::storeFromUploadedFile($file);

$uniqueFile = Dedupler::storeFromPath($absolutePathToFile);

$uniqueFile = Dedupler::storeFromContent($content, 'direct_content_file.ext');

Є ще storeFromStream() для потоків і storeBatch() для масиву файлів.

Важлива деталь: усі ці методи повертають ?UniqueFile. Якщо джерело некоректне або запис на диск чи в базу впав, пакет пише помилку в лог і повертає null, а не кидає виняток. Тож результат треба перевіряти явно, інакше наступне $uniqueFile->id впаде вже з незрозумілою помилкою.

Трейт Deduplable для моделей

Щоб файли належали моделям, додайте трейт:

use Illuminate\Database\Eloquent\Model;
use Maxkhim\Dedupler\Traits\Deduplable;

class Post extends Model
{
  use Deduplable;
}

Трейт дає ті самі методи, але вже з прив'язкою до моделі: storeUploadedFile(), storeLocalFile(), storeContentFile(). Повторне завантаження того самого вмісту поверне той самий запис:

$file1 = $post->storeUploadedFile($sameFile);
$file2 = $post->storeUploadedFile($sameFile);

$file1->id === $file2->id; // true

Файл, який уже є в системі, можна прикріпити до іншої моделі за хешем через attachUniqueFile($sha1Hash), без повторного завантаження. Перевірити зв'язок допоможе hasUniqueFile(), а всі зв'язки моделі доступні через uniqueFileUploads(). Модель має бути збережена: без id прив'язувати файл нема до чого.

Що буде з файлом після останнього detach

Окремого лічильника посилань пакет не тримає - він рахує рядки в проміжній таблиці. $post->detachUniqueFile($sha1Hash) видаляє зв'язок і перевіряє, чи лишилися інші. Якщо ні, за замовчуванням видаляються і фізичний файл, і запис UniqueFile. Поки файл потрібен хоч одній моделі, він лишається на місці.

Саме на цю поведінку поскаржилися в issue #9: для проєктів з аудитом, ревізіями чи архівом замовлень жорстке видалення - ризик. У версії 1.0.6 його можна вимкнути:

DEDUPLER_REMOVE_ORPHAN_ON_DETACH=false

Тоді файли без зв'язків лишаються, а прибирати їх можна вручну командою dedupler:prune. Вона ж чистить зв'язки, що вказують на вже видалені моделі: видалення моделі само по собі зв'язки не знімає. Спершу - завжди з --dry-run:

php artisan dedupler:prune --orphan-relations --orphan-files --dry-run

Обмеження, про які варто знати

  • Звичайний Storage не змінюється. Дедуплікація працює лише для файлів, збережених через Dedupler. Storage::put() і $request->file()->store() пишуть, як і раніше.
  • Старі дублікати лишаються. Пакет не чіпає того, що вже лежить на диску. Команда dedupler:analyse-legacy <шлях> лише рахує дублікати в теці та потенційну економію - це корисно, щоб оцінити, чи варто взагалі заводити пакет.
  • SHA-1. Для звичайних завантажень випадковий збіг хешів практично неможливий. Навмисні колізії SHA-1 продемонстровані ще у 2017 році, тож теоретично зловмисник може підготувати два різні файли з однаковим хешем, і пакет вважатиме їх одним. Для більшості застосунків це не проблема, але для систем, де вміст файлу є доказом, варто пам'ятати.
  • Паралельні завантаження. З коду видно, що пошук і створення запису не загорнуті в транзакцію чи блокування. Якщо два запити одночасно вантажать той самий новий файл, другий може впасти на унікальному ключі - і ви отримаєте null.
  • Зв'язок uniqueFiles(). У версії 1.0.6 він будується лише за deduplable_id, без типу моделі. Якщо трейт мають дві моделі з однаковими id, списки файлів можуть перемішатися. Надійніше йти через uniqueFileUploads(), де поліморфний зв'язок враховує тип.
  • Пакет молодий. Близько 54 зірок, версія 1.0.6, ліцензія MIT. Потребує PHP 8.2+ і підтримує Laravel 10, 11, 12 і 13.

Чи варто брати

Якщо у вас є застосунок, де ті самі документи чи картинки вантажать багато разів - CRM, документообіг, маркетплейс із типовими фото, - Dedupler розв'язує проблему без власної інфраструктури: дві таблиці й окремий диск. Почніть з dedupler:analyse-legacy на копії наявного сховища: цифра потенційної економії швидко покаже, чи є сенс.

Перед продакшеном вирішіть, чи підходить вам автовидалення після останнього detach, перевіряйте null після кожного збереження і подумайте про паралельні завантаження. Код пакета невеликий, і прочитати його цілком - найкращий спосіб зрозуміти, на що ви погоджуєтеся.

0

Читати в документації

Коментарі

Увійдіть, щоб залишити коментар

Будьте першим, хто залишить коментар!

Читайте також

Новини 14 листопада 2026

RFC пропонує хуки життєвого циклу запиту для воркер-режиму PHP

Кевін Дюнгла виніс на обговорення RFC «Request lifecycle hooks for SAPIs with a worker mode»: два нові хуки модулів request_begin і request_end та функції php_request_begin() і php_request_end(). Вони мають замінити крихкі скидання стану, через які FrankenPHP цього року виправляв витік session ID і помилки пам'яті. Ціль - PHP 8.7, голосування ще не призначене.

7
Новини 13 листопада 2026

APM: менеджер залежностей для навичок, промптів і MCP-серверів AI-агентів

Agent Package Manager від Microsoft описує навички, інструкції, плагіни й MCP-сервери агента в apm.yml, ставить їх командою apm install, а apm.lock.yaml фіксує коміти й SHA-256 хеші. Розбираємо, як це лягає на Laravel-проєкт поруч із Laravel Boost і чому lock-файл тут важливий для безпеки.

Вакансії за темою

Black River Express
12 днів тому

Senior PHP Developer

Senior PHP розробник для логістичної платформи: бекенд-сервіси на PHP 8.3+ та Laravel 11 і веб-панель на Filament 3 та Livewire 3. Завдання: REST API, авторизація, інтеграції через API і вебхуки, оптимізація PostgreSQL, черги та кешування. Вимоги: комерційний досвід Laravel, знання PostgreSQL, Redis, WebSockets, CI/CD та діагностики продакшн-проблем. Віддалено, також розглядають сильних Middle+.

ВебФорза Нова
Сьогодні

Програміст PHP (Laravel, WordPress, гіг-контракт)

Розробка на фреймворку Laravel та створення тем і плагінів для WordPress. Завдання включає роботу з jQuery, JS, CSS, HTML і PHP, використання git та оптимізацію швидкості завантаження сторінок. Оформлення за гіг-контрактом з погодинною оплатою.

Senior Full-Stack Developer (PHP / Go / Vue.js)

Senior Full-Stack розробник матиме розвивати й підтримувати існуючі веб-платформи, писати модулі, сервіси та API, а також розробляти клієнтську частину на Vue.js і Vanilla JS. Основний стек: PHP 8.x з Yii2 та/або Laravel, Go для сервісів, MySQL/MariaDB, REST API і Webhooks. Вимоги: досвід від 3 років, знання ООП і шаблонів проєктування, ES6+, Vue.js, SPA, Git, Docker і Composer; англійська B1-B2.

Пакети за темою

Laravel Medialibrary

spatie/laravel-medialibrary

Пакет для асоціювання файлів з Eloquent-моделями. Надає зручний інтерфейс для управління медіа-файлами, пов'язаними з вашими моделями бази даних.

6,175 11.23.13 13 8

Kalimera

welldigit/kalimera

Interactive installer that scaffolds Laravel applications the Well Digit way.

v1.0.0 17