- Исправлена ошибка ReferenceError при загрузке черновиков - Переименована локальная переменная claimId в finalClaimId для избежания конфликта с параметром функции - Обновлена логика извлечения claim_id из разных источников (claim.claim_id, payload.claim_id, body.claim_id, claim.id) - Добавлен fallback на параметр claimId функции для надёжности
257 lines
5.8 KiB
Markdown
257 lines
5.8 KiB
Markdown
# 🗜️ PDF Compression в n8n
|
||
|
||
## 📋 Проблема
|
||
Пользователь загружает PDF 5-10 MB → долгая обработка OCR
|
||
|
||
## ✅ Решение: 2-уровневая система
|
||
|
||
---
|
||
|
||
## 🎯 Уровень 1: Frontend (React)
|
||
|
||
**Что делаем:**
|
||
- JPG/PNG → сжатие до 2MB → конвертация в PDF
|
||
- PDF < 5MB → пропускаем
|
||
- PDF > 10MB → **отклоняем** с сообщением
|
||
|
||
**Код:** `frontend/src/utils/pdfConverter.ts` ✅ УЖЕ ГОТОВО
|
||
|
||
---
|
||
|
||
## 🎯 Уровень 2: Backend (n8n)
|
||
|
||
### Workflow для сжатия PDF > 5MB
|
||
|
||
```
|
||
Webhook (file upload)
|
||
↓
|
||
IF Node: file_size > 5 MB?
|
||
├─ FALSE → S3 Upload (оригинал)
|
||
└─ TRUE → Python Code Node (compress)
|
||
↓
|
||
S3 Upload (compressed)
|
||
```
|
||
|
||
---
|
||
|
||
## 🐍 Python Code Node - PDF Compression
|
||
|
||
### Установка библиотеки в n8n
|
||
|
||
```bash
|
||
# В контейнере n8n
|
||
docker exec -it <n8n_container_name> sh
|
||
apk add --no-cache python3 py3-pip
|
||
pip3 install pypdf
|
||
```
|
||
|
||
### Code Node конфигурация
|
||
|
||
**Language:** Python
|
||
**Mode:** Run Once for All Items
|
||
|
||
**Code:**
|
||
```python
|
||
import io
|
||
from pypdf import PdfReader, PdfWriter
|
||
|
||
# Получаем binary data из предыдущей ноды
|
||
input_data = items[0].binary['data']
|
||
pdf_bytes = input_data
|
||
|
||
# Читаем PDF
|
||
reader = PdfReader(io.BytesIO(pdf_bytes))
|
||
writer = PdfWriter()
|
||
|
||
# Копируем страницы с оптимизацией
|
||
for page in reader.pages:
|
||
# Удаляем неиспользуемые объекты
|
||
page.compress_content_streams()
|
||
writer.add_page(page)
|
||
|
||
# Применяем сжатие
|
||
writer.compress_identical_objects()
|
||
writer.remove_duplication()
|
||
|
||
# Сжимаем изображения (если есть)
|
||
for page in writer.pages:
|
||
for img in page.images:
|
||
img.replace(img.image, quality=70)
|
||
|
||
# Выводим в bytes
|
||
output = io.BytesIO()
|
||
writer.write(output)
|
||
compressed_bytes = output.getvalue()
|
||
|
||
# Логируем результат
|
||
original_size = len(pdf_bytes) / (1024 * 1024)
|
||
compressed_size = len(compressed_bytes) / (1024 * 1024)
|
||
compression_ratio = ((original_size - compressed_size) / original_size) * 100
|
||
|
||
print(f"✅ Compressed: {original_size:.2f}MB → {compressed_size:.2f}MB ({compression_ratio:.1f}% reduction)")
|
||
|
||
# Возвращаем binary data
|
||
return {
|
||
'binary': {
|
||
'data': compressed_bytes
|
||
},
|
||
'json': {
|
||
'original_size_mb': round(original_size, 2),
|
||
'compressed_size_mb': round(compressed_size, 2),
|
||
'compression_ratio': round(compression_ratio, 1),
|
||
'success': True
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🔧 Вариант 2: Execute Command (Ghostscript)
|
||
|
||
**Требует:** `ghostscript` установлен в системе
|
||
|
||
### Execute Command Node:
|
||
|
||
```bash
|
||
#!/bin/bash
|
||
|
||
INPUT="/tmp/input_{{ $json.file_id }}.pdf"
|
||
OUTPUT="/tmp/output_{{ $json.file_id }}.pdf"
|
||
|
||
# Сохраняем binary в файл
|
||
echo "{{ $binary.data }}" | base64 -d > "$INPUT"
|
||
|
||
# Сжимаем через Ghostscript
|
||
gs -sDEVICE=pdfwrite \
|
||
-dCompatibilityLevel=1.4 \
|
||
-dPDFSETTINGS=/ebook \
|
||
-dNOPAUSE \
|
||
-dQUIET \
|
||
-dBATCH \
|
||
-sOutputFile="$OUTPUT" \
|
||
"$INPUT"
|
||
|
||
# Выводим compressed PDF
|
||
cat "$OUTPUT" | base64
|
||
|
||
# Cleanup
|
||
rm -f "$INPUT" "$OUTPUT"
|
||
```
|
||
|
||
**Параметры `-dPDFSETTINGS`:**
|
||
- `/screen` - 72 DPI (минимальное качество, максимальное сжатие)
|
||
- `/ebook` - 150 DPI ⭐ **рекомендуется**
|
||
- `/printer` - 300 DPI
|
||
- `/prepress` - 300 DPI (максимальное качество)
|
||
|
||
---
|
||
|
||
## 🔄 Полный Workflow
|
||
|
||
### 1. Webhook (File Upload)
|
||
|
||
**Input:**
|
||
```json
|
||
{
|
||
"claim_id": "CLM-2025-10-26-ABC123",
|
||
"file_type": "policy_scan",
|
||
"filename": "policy.pdf",
|
||
"voucher": "E1000-302372730",
|
||
"session_id": "sess-xyz-456"
|
||
}
|
||
```
|
||
|
||
**Binary Data:** `data` (PDF file)
|
||
|
||
---
|
||
|
||
### 2. IF Node: Check File Size
|
||
|
||
**Condition:**
|
||
```
|
||
{{ $binary.data.length }} > 5242880
|
||
```
|
||
(5MB = 5 * 1024 * 1024 bytes)
|
||
|
||
---
|
||
|
||
### 3a. FALSE → Direct Upload
|
||
|
||
**S3 Upload Node** → PostgreSQL
|
||
|
||
---
|
||
|
||
### 3b. TRUE → Compress First
|
||
|
||
```
|
||
Python Code (compress)
|
||
↓
|
||
Set Binary Data
|
||
↓
|
||
S3 Upload (compressed)
|
||
↓
|
||
PostgreSQL (update file_size)
|
||
```
|
||
|
||
---
|
||
|
||
## 📊 Результаты сжатия
|
||
|
||
| Метод | Скорость | Сжатие | Качество |
|
||
|-------|----------|--------|----------|
|
||
| **pypdf** | Быстро | 30-50% | Хорошее ⭐ |
|
||
| **Ghostscript /ebook** | Средне | 50-70% | Среднее |
|
||
| **Ghostscript /screen** | Средне | 70-85% | Низкое |
|
||
| **Frontend (jspdf)** | Моментально | 60-80% | Хорошее ✅ |
|
||
|
||
---
|
||
|
||
## 🎯 Итоговая стратегия
|
||
|
||
```
|
||
📱 Пользователь загружает файл
|
||
↓
|
||
🔍 Frontend проверка:
|
||
├─ JPG/PNG → compress + convert → PDF (✅ готово)
|
||
├─ PDF < 5MB → отправить как есть
|
||
├─ PDF 5-10MB → отправить (n8n сожмёт)
|
||
└─ PDF > 10MB → ❌ отклонить
|
||
|
||
🚀 n8n workflow:
|
||
├─ file_size < 5MB → S3 + OCR
|
||
└─ file_size > 5MB → Python compress → S3 + OCR
|
||
```
|
||
|
||
---
|
||
|
||
## 🧪 Тестирование
|
||
|
||
### curl пример:
|
||
|
||
```bash
|
||
# Создаём большой PDF для теста
|
||
curl -o large.pdf https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf
|
||
|
||
# Отправляем в n8n
|
||
curl -X POST \
|
||
-F "claim_id=CLM-TEST-001" \
|
||
-F "file_type=policy_scan" \
|
||
-F "fileInput=@large.pdf" \
|
||
-F "voucher=TEST-123" \
|
||
-F "session_id=sess-test" \
|
||
https://n8n.clientright.pro/webhook/7e2abc64-eaca-4671-86e4-12786700fe95
|
||
```
|
||
|
||
---
|
||
|
||
## ✅ Готово!
|
||
|
||
**Frontend:** ✅ Ограничение 10MB + предупреждение
|
||
**n8n:** ⏳ Нужно добавить Python Code Node
|
||
|
||
**Следующий шаг:** Добавить Python Code Node в workflow для файлов > 5MB
|
||
|
||
|
||
|
||
|