🎯 Основные изменения: Backend: - ✅ Добавлен SSE endpoint для real-time событий (/api/v1/events/{task_id}) - ✅ Redis Pub/Sub для публикации/подписки на события OCR/Vision - ✅ Удален aioboto3 из requirements.txt (конфликт зависимостей) - ✅ Добавлен OCR worker (deprecated, логика перенесена в n8n) Frontend (React): - ✅ Автогенерация claim_id и session_id - ✅ Клиентская конвертация файлов в PDF (JPG/PNG/HEIC/WEBP) - ✅ Сжатие изображений до 2MB перед конвертацией - ✅ SSE подписка на события OCR/Vision в Step1Policy - ✅ Валидация документов (полис vs неподходящий контент) - ✅ Real-time прогресс загрузки и обработки файлов - ✅ Интеграция с n8n webhooks для проверки полиса и загрузки файлов n8n Workflows: - ✅ Проверка полиса в MySQL + запись в PostgreSQL - ✅ Загрузка файлов в S3 + OCR + Vision AI - ✅ Публикация событий в Redis через backend API - ✅ Валидация документов (распознавание полисов ERV) Документация: - 📝 N8N_INTEGRATION.md - интеграция с n8n - 📝 N8N_SQL_QUERIES.md - SQL запросы для workflows - 📝 N8N_PDF_COMPRESS.md - сжатие PDF - 📝 N8N_STIRLING_COMPRESS.md - интеграция Stirling-PDF Утилиты: - 🔧 monitor_redis.py/sh - мониторинг Redis Pub/Sub - 🔧 test_redis_events.sh - тестирование событий - 🔧 pdfConverter.ts - клиентская конвертация в PDF Архитектура: React → n8n webhooks (sync) → MySQL/PostgreSQL/S3 → n8n workflows (async) → OCR/Vision → Redis Pub/Sub → SSE → React
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
|
||
|
||
|
||
|
||
|