Кейс: 318 ГБ фото в OpenCart зупинили завантаження товарів
Інтернет-магазин одягу на OpenCart перестав приймати нові фото товарів: менеджер завантажувала папки через FTP, а Total Commander зупинявся на 1% з помилками 550 No such file or directory і «Не вдається створити віддалений каталог». Причина була не у FTP: фото товарів займали 318 ГБ на диску в 394 ГБ, і серверу забракло місця для запису. Ми звільнили місце, власним скриптом видалили фото товарів, яких уже немає в каталозі, і перевели зображення у WebP. Усе — в межах обслуговування сайту.
Що бачив клієнт
Каталог магазину наповнюється так: для кожного товару менеджер створює на FTP папку з артикулом і завантажує туди фото. Одна партія — кілька десятків товарів і сотні мегабайт: окремий знімок важив 6,2 МБ. В якийсь момент завантаження почало обриватися:
451 Append/Restart not permitted— сервер не дав докачати файл після обриву;550 No such file or directoryі «Не вдається створити віддалений каталог» — сервер не створює папку для нового товару.
Після п’яти спроб завантаження не йшло далі 1%, а згодом перестало стартувати взагалі. На боці клієнта нічого не змінювалося, тому першою підозрою були FTP-клієнт або мережа.
Діагностика: куди поділося місце
Замість налаштувань Total Commander ми перевірили сам сервер. Картину дали три команди:
df -i— inodes (ліміт кількості файлів) зайняті лише на 2%, тож справа не в кількості файлів;df -h— основний розділ 394 ГБ, з них зайнято 316 ГБ;du -ahx / | sort -rh | head -n 20— майже все це місце займає папкаimageмагазину.
| Папка | Розмір | Що в ній |
|---|---|---|
image/catalog | 282 ГБ | оригінали фото товарів у тому вигляді, як їх завантажили |
image/cache | 36 ГБ | зменшені копії, які OpenCart генерує сам |
Разом image | 318 ГБ | 81% усього диска сервера |
Чому OpenCart так розростається
Це не збій, а три особливості OpenCart, які проявляються на великих каталогах:
- Оригінал зберігається як є. OpenCart не зменшує завантажене фото. Типовий знімок у цьому магазині — 2235×3365 пікселів і 2,3 МБ, хоча на сайті він ніде не показується в такому розмірі.
- На кожне фото — набір копій. Для картки товару, списку, кошика, порівняння та інших блоків OpenCart створює окремі мініатюри в
image/cache. Тут це близько 8 розмірів на кожне фото. - Видалений товар не забирає з собою фото. Коли товар видаляють в адмінці, зникає лише запис у базі, а файли лишаються на диску. Асортимент за роки змінився, а фото старих колекцій нікуди не ділися.
Каталог росте з кожною поставкою, тож диск закінчиться рано чи пізно. Питання лише в тому, коли.
Що ми зробили
1. Звільнили місце, щоб відновити завантаження
Спершу прибрали те, що можна видалити без ризику, — кеш мініатюр image/cache. OpenCart створює його заново, коли сторінку відкривають уперше. Після цього FTP знову почав створювати папки, і менеджер продовжила наповнення.
2. Написали скрипт пошуку «осиротілих» фото
Готові модулі очищення для OpenCart ми відкинули: вони не знають про нестандартний імпорт товарів у цьому магазині і можуть прийняти живі фото за сміття. Тому написали свій скрипт із простою логікою:
- зібрати з бази всі шляхи до фото товарів: головні (
oc_product) і з галереї (oc_product_image); - пройти папку з оригіналами і звірити кожен файл зі списком;
- файл, якого немає в базі, видалити; папку товару, що стала порожньою, — теж.
Ядро перевірки займає кілька рядків:
$used = [];
foreach (['product', 'product_image'] as $table) {
$res = $db->query("SELECT image FROM " . DB_PREFIX . $table . " WHERE image <> ''");
while ($row = $res->fetch_assoc()) {
$used[$row['image']] = true; // catalog/nobrand/038939/2.jpg
}
}
// path on disk -> path as stored in the database
$relative = str_replace(DIR_IMAGE, '', $fullPath);
if (!isset($used[$relative])) {
unlink($fullPath); // dry run: echo $relative;
} Найбільший ризик такого скрипта — стерти живі фото. Тому працювали так:
- Спочатку — режим імітації. Скрипт нічого не видаляв, а показував перші 100 файлів-кандидатів і приклади шляхів із бази. Так ми переконалися, що шляхи на диску і в базі записані однаково (
catalog/..., без слеша на початку). Якби вони відрізнялися хоч на один символ, скрипт вважав би сміттям геть усе. - Тільки папка з фото товарів. Картинки категорій, банерів і виробників лежать окремо, і скрипт їх не торкається.
- Запуск з консолі, а не з браузера. Обхід сотень гігабайтів триває довше, ніж дозволяє таймаут вебсервера. Запуск, обірваний посередині, лишив би незрозуміло, що вже видалено. Через PHP CLI скрипт працює до кінця і виводить прогрес кожні 5000 файлів.
- Після роботи скрипт видалили з сервера. Залишений у корені сайту, він дав би будь-кому змогу стерти фото, просто відкривши посилання.
3. Оптимізували фото і перевели у WebP
Спершу спробували стиснути оригінали без втрат утилітою jpegoptim. На реальних фото магазину це майже нічого не дало: 2 274 925 → 2 273 571 байт, тобто 0,06%. Знімки з фотостудії вже збережені з хорошим стисненням, і без втрат якості з них нічого не вичавиш.
Тому ми написали власний скрипт, який проходить кожне зображення, оптимізує його і конвертує у WebP. Тут є нюанс OpenCart: шляхи до фото зберігаються в базі з розширенням .jpg. Якщо просто замінити файли на диску на WebP, картинки на сайті зникнуть. Тому зміна формату має йти разом зі зміною шляхів у базі.
І ще одне: масове стиснення сотень тисяч файлів сильно навантажує диск. Його варто запускати в години найменшого трафіку і зі зниженим пріоритетом (nice, ionice), щоб не заважати крон-задачам та імпорту товарів.
Результат
- Менеджер знову завантажує фото через FTP без помилок.
- На диску більше немає фото товарів, яких уже немає в каталозі.
- Фото на сайті віддаються у WebP, тож сторінки каталогу стали легшими. Це особливо помітно з мобільного інтернету.
Як не довести свій магазин до такого
- Налаштуйте сповіщення про заповнення диска, наприклад на 80%. Помилка FTP — це вже пізній симптом.
- Зменшуйте фото до завантаження. Оригінал на 3000+ пікселів лежить на диску назавжди, хоча сайту потрібен значно менший.
- Раз на квартал перевіряйте розмір папки
imageі чистьте фото видалених товарів. - Перед масовим видаленням — бекап і режим імітації. Відновити стерті фото без копії неможливо.
Такі задачі ми вирішуємо в межах обслуговування сайтів: стежимо за станом сервера і сайту, щоб проблема не дійшла до покупців. Якщо ваш магазин на OpenCart сповільнився або сервер «з’їдає» місце, напишіть нам — подивимось.
Часті питання
Так. Там лише зменшені копії фото, і OpenCart створить їх заново, коли сторінки відкриють уперше. Перші відкриття сторінок після очищення будуть трохи повільнішими.
Стандартний OpenCart при видаленні товару прибирає лише записи в базі даних, а файли зображень лишаються на диску. Їх доводиться чистити окремо — вручну або скриптом, який звіряє файли з базою.
Виконайте на сервері df -h (вільне місце) і df -i (ліміт кількості файлів), а також перевірте дискову квоту FTP-користувача в панелі хостингу. Типові симптоми — помилка 550 при створенні папки і відмова докачувати файли.
Тільки разом зі зміною шляхів у базі даних: OpenCart зберігає їх з розширенням .jpg, і без цього фото на сайті зникнуть.