Кейс: 318 ГБ фото в OpenCart зупинили завантаження товарів

30.10.2026 • 6 переглядів • Категорія: Обслуговування сайтів

Кейс: 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/catalog282 ГБоригінали фото товарів у тому вигляді, як їх завантажили
image/cache36 ГБзменшені копії, які OpenCart генерує сам
Разом image318 ГБ81% усього диска сервера

Чому OpenCart так розростається

Це не збій, а три особливості OpenCart, які проявляються на великих каталогах:

  1. Оригінал зберігається як є. OpenCart не зменшує завантажене фото. Типовий знімок у цьому магазині — 2235×3365 пікселів і 2,3 МБ, хоча на сайті він ніде не показується в такому розмірі.
  2. На кожне фото — набір копій. Для картки товару, списку, кошика, порівняння та інших блоків OpenCart створює окремі мініатюри в image/cache. Тут це близько 8 розмірів на кожне фото.
  3. Видалений товар не забирає з собою фото. Коли товар видаляють в адмінці, зникає лише запис у базі, а файли лишаються на диску. Асортимент за роки змінився, а фото старих колекцій нікуди не ділися.

Каталог росте з кожною поставкою, тож диск закінчиться рано чи пізно. Питання лише в тому, коли.

Що ми зробили

1. Звільнили місце, щоб відновити завантаження

Спершу прибрали те, що можна видалити без ризику, — кеш мініатюр image/cache. OpenCart створює його заново, коли сторінку відкривають уперше. Після цього FTP знову почав створювати папки, і менеджер продовжила наповнення.

2. Написали скрипт пошуку «осиротілих» фото

Готові модулі очищення для OpenCart ми відкинули: вони не знають про нестандартний імпорт товарів у цьому магазині і можуть прийняти живі фото за сміття. Тому написали свій скрипт із простою логікою:

  1. зібрати з бази всі шляхи до фото товарів: головні (oc_product) і з галереї (oc_product_image);
  2. пройти папку з оригіналами і звірити кожен файл зі списком;
  3. файл, якого немає в базі, видалити; папку товару, що стала порожньою, — теж.

Ядро перевірки займає кілька рядків:

$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, і без цього фото на сайті зникнуть.

Telegram
Написати в Telegram Відповімо за 5 хвилин