Зауважте: коли сайт генерує прев'ю для тисяч завантажених 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 година вебінару).
Процес роботи
- Аналіз — вивчаємо потік завантажень, вимірюємо навантаження.
- Проектування — обираємо інструменти, налаштовуємо оточення.
- Розробка — пишемо сервіс конвертації, jobs, ендпоінти.
- Тестування — навантажувальне (мінімум 1000 файлів).
- Деплой і моніторинг — налаштування сповіщень при помилках.
Строки та гарантії
Налаштування базової конвертації — від 2 до 5 днів залежно від складності інтеграції. Ми гарантуємо стабільну роботу при навантаженні до 5000 файлів на день. Наш досвід: понад 5 років на ринку, 200+ проектів з документообігом. Отримайте консультацію з налаштування конвертації — наші інженери допоможуть підібрати оптимальну конфігурацію під ваше навантаження. Зв'яжіться з нами — оцінимо ваше завдання протягом дня.







