Налаштування серверної конвертації PDF і DOCX

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

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

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

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Налаштування серверної конвертації PDF і DOCX
Середній
~2-3 дні
Часті запитання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1358
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1251
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    956
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1188
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    929
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Розробка веб-сайту для компанії ФІКСПЕР
    947

Зауважте: коли сайт генерує прев'ю для тисяч завантажених PDF, кожен виклик Ghostscript на льоту може завантажити CPU на 40% і спричинити простої. Особливо якщо користувачі завантажують 100-сторінкові контракти — конвертація всього документа в JPEG забирає час і пам'ять. Використання безкоштовних інструментів економить до 100 000 рублей на рік порівняно з комерційними аналогами. Ми вирішуємо це завдання за допомогою зв'язки Ghostscript і LibreOffice, налаштованої на асинхронне оброблення через черги. Це дозволяє обробляти до 5000 файлів на день без блокування користувача. Серверна конвертація PDF — наш профіль. Замовте налаштування конвертації — і забудьте про проблеми з продуктивністю. Також ми надаємо готовий сервіс конвертації Laravel, який легко інтегрується у ваш проект.

Економія бюджету на ліцензуванні досягається за рахунок використання безкоштовних інструментів: Ghostscript (AGPL) і LibreOffice (MPL). Зниження вартості супроводу забезпечує автоматизація — не потрібно вручну очищати тимчасові профілі чи моніторити помилки. Ми оптимізуємо процеси для мінімізації часу відповіді та пікового споживання пам'яті. Для прев'ю достатньо 150 DPI, що дає 40% економії часу порівняно з 300 DPI.

Проблеми, які ми вирішуємо

  • Швидке прев'ю PDF (перші сторінки) без завантаження всього файлу клієнтом. Ghostscript з параметрами -dFirstPage і -dLastPage генерує зображення лише потрібного діапазону.
  • Конвертація DOCX/DOC у PDF зі збереженням форматування та шрифтів. LibreOffice headless справляється з будь-яким форматуванням, включаючи таблиці, виноски та вбудовані шрифти.
  • Автоматичне оброблення у фоні через чергу завдань, щоб користувач не чекав завершення конвертації.

Чому Ghostscript, а не ImageMagick?

Ghostscript спеціалізується на PostScript/PDF — він точніше передає кольори та шрифти. ImageMagick використовує Ghostscript під капотом, але додає етапи, що може спотворити картинку. Для прев'ю різниця непомітна, але для високоякісних зображень (300 dpi) Ghostscript надійніший. Порівняємо в таблиці:

Інструмент Швидкість (сторінок/сек) Якість Безпека Ліцензія
Ghostscript CLI ~3 стр/сек (прев'ю 150 dpi) Відмінна Через -dSAFER AGPL
ImageMagick + GS ~2 стр/сек Добра Потребує налаштування політик Apache 2.0
PDFium (Chrome) ~5 стр/сек Добра Через sandbox BSD

Для продакшену ми обираємо Ghostscript — він простіший у налаштуванні, менше шансів на витік пам'яті. Ghostscript на 30% швидший за ImageMagick при конвертації PDF в JPEG з однаковою якістю.

Як налаштувати конвертацію DOCX у PDF?

LibreOffice у headless-режимі — стандарт індустрії. Підтримує всі популярні формати і не потребує купівлі ліцензії, що економить бюджет на супроводі.

PDF → Image

Стандартний інструмент — Ghostscript (gs). Присутній у більшості Linux-дистрибутивів, добре справляється з векторною графікою та шрифтами:

apt install ghostscript

Конвертація першої сторінки в JPEG:

gs -dNOPAUSE -dBATCH -sDEVICE=jpeg -r150 \
   -dFirstPage=1 -dLastPage=1 \
   -sOutputFile=page_%02d.jpg \
   input.pdf

-r150 — DPI (150 достатньо для прев'ю, 300 — для повної якості). -dFirstPage/-dLastPage — діапазон сторінок.

DOCX → PDF

LibreOffice у headless-режимі — найнадійніший безкоштовний інструмент. При паралельних викликах виникає конфлікт через спільний профіль. Рішення: унікальний профіль через --env:UserInstallation:

libreoffice --headless \
  "--env:UserInstallation=file:///tmp/lo_profile_$$" \
  --convert-to pdf \
  --outdir /tmp/output \
  /tmp/input/document.docx

$$ — PID процесу, забезпечує унікальність.

Забезпечення безпеки при конвертації документів

Користувацькі файли — потенційний вектор атаки. Ghostscript documentation recommends using the -dSAFER flag for secure operation. Обов'язкові заходи:

  • Перевіряти MIME-тип через finfo_file(), а не лише розширення.
  • Запускати конвертацію в окремому користувачі без прав на запис у корінь проекту.
  • Обмежувати розмір файлу (max: 50000 у правилах валідації — 50 МБ).
  • Не зберігати завантажені файли в публічно доступному шляху до завершення перевірки.
$request->validate([
    'file' => [
        'required',
        'file',
        'max:51200', // 50 MB
        'mimes:pdf,docx,doc',
    ],
]);

// Додаткова перевірка реального MIME
$finfo = new \finfo(FILEINFO_MIME_TYPE);
$mime  = $finfo->file($request->file('file')->getRealPath());
abort_unless(in_array($mime, ['application/pdf', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document']), 422);

Це запобігає атакам і знижує вартість супроводу за рахунок автоматизації. Впровадження черг знижує навантаження на сервер, скорочуючи витрати на хмарні ресурси на 30-40%, що еквівалентно економії близько 50 000 рублей щомісяця.

Детальніше про налаштування черг Для фонової обробки ми використовуємо чергу завдань (Jobs). Це дозволяє не блокувати користувача і обробляти безліч файлів паралельно. Налаштування включає: - Конфігурація черги в Laravel (database або Redis). - Supervisor для моніторингу воркерів. - Обробка помилок і повторні спроби. - Моніторинг через Laravel Horizon або подібні інструменти.

PHP-сервіс конвертації

namespace App\Services;

use Illuminate\Support\Str;

class DocumentConversionService
{
    public function pdfToImages(
        string $pdfPath,
        string $outputDir,
        int    $dpi        = 150,
        string $format     = 'jpg',
        ?int   $maxPages   = null
    ): array {
        @mkdir($outputDir, 0755, true);

        $pages    = $this->getPdfPageCount($pdfPath);
        $maxPages = $maxPages ? min($maxPages, $pages) : $pages;

        $outputPattern = escapeshellarg("{$outputDir}/page_%03d.{$format}");

        $cmd = implode(' ', [
            'gs',
            '-dNOPAUSE -dBATCH -dSAFER',
            '-sDEVICE=' . ($format === 'png' ? 'png16m' : 'jpeg'),
            "-r{$dpi}",
            "-dFirstPage=1 -dLastPage={$maxPages}",
            '-dJPEGQ=85',
            "-sOutputFile={$outputPattern}",
            escapeshellarg($pdfPath),
            '2>&1',
        ]);

        exec($cmd, $output, $exitCode);

        if ($exitCode !== 0) {
            throw new \RuntimeException(
                "PDF conversion failed: " . implode("\n", $output)
            );
        }

        // Збираємо список створених файлів
        $files = [];
        for ($i = 1; $i <= $maxPages; $i++) {
            $file = sprintf("{$outputDir}/page_%03d.{$format}", $i);
            if (file_exists($file)) {
                $files[] = $file;
            }
        }

        return $files;
    }

    public function docxToPdf(string $docxPath, string $outputDir): string
    {
        @mkdir($outputDir, 0755, true);

        $profileDir = sys_get_temp_dir() . '/lo_profile_' . Str::random(8);
        @mkdir($profileDir, 0755, true);

        $cmd = implode(' ', [
            'libreoffice',
            '--headless',
            "--env:UserInstallation=" . escapeshellarg("file://{$profileDir}"),
            '--convert-to pdf',
            '--outdir ' . escapeshellarg($outputDir),
            escapeshellarg($docxPath),
            '2>&1',
        ]);

        exec($cmd, $output, $exitCode);

        // Видаляємо тимчасовий профіль
        $this->rmdir($profileDir);

        if ($exitCode !== 0) {
            throw new \RuntimeException(
                "DOCX to PDF failed: " . implode("\n", $output)
            );
        }

        $outputFile = $outputDir . '/' . pathinfo($docxPath, PATHINFO_FILENAME) . '.pdf';
        if (!file_exists($outputFile)) {
            throw new \RuntimeException("Output PDF not found after conversion");
        }

        return $outputFile;
    }

    public function getPdfPageCount(string $path): int
    {
        $cmd    = "pdfinfo " . escapeshellarg($path) . " 2>/dev/null | grep 'Pages:' | awk '{print $2}'";
        $output = shell_exec($cmd);
        return max(1, (int) trim($output ?? '1'));
    }

    private function rmdir(string $dir): void
    {
        if (!is_dir($dir)) return;
        $items = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS),
            \RecursiveIteratorIterator::CHILD_FIRST
        );
        foreach ($items as $item) {
            $item->isDir() ? rmdir($item->getRealPath()) : unlink($item->getRealPath());
        }
        rmdir($dir);
    }
}

Таблиця типових таймінгів конвертації

Розмір файлу PDF→JPEG (1 стор, 150 DPI) DOCX→PDF
< 1 MB 0.3 сек 1.5 сек
1–10 MB 1.2 сек 3.5 сек
> 10 MB 3.5 сек 7 сек

Що входить у налаштування серверної конвертації?

  • Розгортання та налаштування Ghostscript/LibreOffice на сервері.
  • Розробка сервісу-обгортки з відмовостійкістю (обробка помилок, очищення тимчасових профілів).
  • Налаштування черг (Jobs) для фонової обробки.
  • Інтеграція з вашим застосунком (REST API або події).
  • Документація щодо API та експлуатації.
  • Навчання команди (1 година вебінару).

Процес роботи

  1. Аналіз — вивчаємо потік завантажень, вимірюємо навантаження.
  2. Проектування — обираємо інструменти, налаштовуємо оточення.
  3. Розробка — пишемо сервіс конвертації, jobs, ендпоінти.
  4. Тестування — навантажувальне (мінімум 1000 файлів).
  5. Деплой і моніторинг — налаштування сповіщень при помилках.

Строки та гарантії

Налаштування базової конвертації — від 2 до 5 днів залежно від складності інтеграції. Ми гарантуємо стабільну роботу при навантаженні до 5000 файлів на день. Наш досвід: понад 5 років на ринку, 200+ проектів з документообігом. Отримайте консультацію з налаштування конвертації — наші інженери допоможуть підібрати оптимальну конфігурацію під ваше навантаження. Зв'яжіться з нами — оцінимо ваше завдання протягом дня.

Послуги бекенд-розробки: production-grade надійність

На production-сервері о 3:14 ночі черга Laravel Jobs перестала оброблятися — 40 000 необроблених завдань у Redis. Причина: worker упав через memory leak у статичній змінній Eloquent observer, supervisor не перезапустив через misconfigured stopwaitsecs. Ми розбирали такий інцидент на проекті з 500 RPS: діагностика 4 години, фікс — 20 хвилин. Щоб ви не втрачали гроші, пропонуємо послуги бекенд-розробки з акцентом на production-grade надійність — 10+ років досвіду, 50+ проектів, 5 років на ринку. Оцінимо ваш проект за 2 дні.

Які проблеми вирішуємо

N+1 запити: головний вбивця швидкості

N+1 — найпоширеніша причина повільних сторінок у Laravel-додатках. Стандартна історія: сторінка працювала нормально на dev з 10 записами, на production з 10 000 — 8-секундне завантаження.

Laravel Debugbar у dev-оточенні показує кількість запитів. Більше 20 — сигнал для audit.

Model::preventLazyLoading(! app()->isProduction());

Telescope для профілювання: логує всі запити, jobs, mail, notifications з деталізацією. Після впровадження eager loading час завантаження сторінки падає з 8 с до 0.3 с — у 27 разів.

Memory leak у статичних змінних

У Laravel Octane або Swoole додаток тримається в пам’яті між запитами. Статичні змінні не скидаються — призводять до неконтрольованого росту пам’яті. Використовуємо defer-функції та контейнерні біндинги для коректного скидання стану.

Неправильний connection pool

Rails, Laravel, Django відкривають нове з'єднання PostgreSQL на кожен PHP/Python процес. 100 воркерів — 100 з'єднань. PostgreSQL деградує від 200+ активних з'єднань через overhead на управління.

PgBouncer у transaction pooling: 1000 воркерів → 20–50 реальних з'єднань. Це знижує latency на 40% та зменшує витрати на хостинг на 30% — при середній вартості хостингу $2,000/міс економить $600/міс. GIN-індекс для JSONB до 100 разів швидший за B-tree при пошуку.

Як Octane справляється з високим навантаженням?

Laravel Octane (RoadRunner або Swoole) прибирає overhead bootstrap на кожен HTTP-запит. Приріст: 3–8x на синтетичних бенчмарках, 2–4x на реальних додатках. Важливо: не зберігати стан у статичних змінних — застосовуємо це на проектах >1000 RPS.

Як PostgreSQL допомагає уникнути повільних запитів?

Використовуємо composite indexes для WHERE + ORDER BY, partial indexes для фільтрів з високою селективністю, GIN-індекси для JSONB та full-text search. to_tsvector + GIN замість LIKE '%query%' — запобігає seq scan навіть на мільйонах записів. Аналізуємо плани через EXPLAIN ANALYZE та pg_stat_statements.

Як обрати стек для вашого проекту?

Стек Коли використовувати
Laravel + Octane CRUD, бізнес-логіка, REST/GraphQL API, адмінки
Node.js (Fastify) Realtime WebSocket, streaming, serverless, висока I/O concurrency
Go Високонавантажені мікросервіси (>10k RPS), gRPC, DevOps-інструменти
Django + DRF ML-пайплайни, інтеграція з AI, складна обробка даних
Ruby on Rails Швидкий MVP з багатим екосистемою гемів

Node.js виправданий для realtime: Laravel публікує події в Redis Pub/Sub, Node.js підписується та транслює клієнтам. Go — для goroutines (10k з'єднань на сервер — норма), але розробка повільніша, ніж Laravel.

Чому Redis критичний для продуктивності?

Redis виконує кілька ролей:

Роль Деталі
Кеш Кешування результатів важких запитів, фрагментів HTML
Черги Backend для Laravel Queue / Celery
Session store Distributed sessions в multi-instance оточенні
Pub/Sub Realtime події між сервісами
Rate limiting Sliding window counters для API throttling
Leaderboards Sorted Sets для рейтингів

Redis Cluster для горизонтального масштабування, Sentinel для автоматичного failover. Замовте консультацію щодо оптимізації Redis для вашого проекту.

Що входить в роботу під ключ

  • Архітектурне проектування (документація API, схема БД, діаграма сервісів)
  • Реалізація за узгодженим ТЗ з code review
  • Налаштування CI/CD (GitHub Actions, Docker), моніторингу (Sentry, Grafana), алертингу
  • Навантажувальне тестування (k6, wrk) зі звітом
  • Передача вихідних кодів, доступів, інструкція з деплою
  • Навчання команди замовника (2–3 сесії)
  • Гарантійна підтримка 1 місяць після здачі

Орієнтири по термінах

Задача Термін
REST API для мобільного/SPA (середня складність) 6–12 тижнів
Backend зі складною бізнес-логікою + інтеграції 12–20 тижнів
Високонавантажений сервіс на Go 8–16 тижнів
Міграція legacy PHP на Laravel 16–32 тижні

Вартість розраховується індивідуально після аналізу вимог до навантаження, інтеграцій та бізнес-логіки. Зв'яжіться з нами для безкоштовного аудиту вашого поточного backend — отримайте план оптимізації за 2 дні. Замовте консультацію та дізнайтеся, як знизити витрати на інфраструктуру на 30% без втрати продуктивності.