Przejdź do głównej zawartości

Konwersja dokumentów Office do PDF przy użyciu Gotenberg

Mostek Gotenberg konwertuje dokument Office do PDF. Wysyła dokument do mikrousługi Gotenberg przez HTTPS i zwraca bajty PDF. Usługę opisujesz niezmienną konfiguracją GotenbergConfig, podłączasz klienta PSR-18 oraz fabryki PSR-17 do GotenbergBridge, sprawdzasz stan usługi i konwertujesz plik z dysku albo bajty z pamięci. Przewodnik omawia wykrywanie formatu po rozszerzeniu, sondę stanu, kontrakt typowanych błędów oraz przekazanie wyniku do przetwarzania końcowego w NextPDF.

Wymagania wstępne, od razu na początku:

  • Rdzeń NextPDF oraz pakiet nextpdf/gotenberg są zainstalowane.
  • Usługa Gotenberg jest dostępna przez HTTPS. Mostek odrzuca zwykły adres URL z http://, zanim z procesu wyjdzie jakiekolwiek żądanie.
  • Klient PSR-18 oraz fabryki żądań i strumieni PSR-17 są zainstalowane. Aby używać przypinania DNS i TLS, dostarczasz dodatkowo fabrykę odpowiedzi PSR-17.
  • Dane wejściowe to jeden z sześciu rozpoznawanych formatów Office: .docx, .xlsx, .pptx, .odt, .ods lub .odp. Mostek odrzuca każde inne rozszerzenie, zgłaszając ValueError.

To przewodnik praktyczny. Kompletny, gotowy do uruchomienia program znajdziesz w przewodniku Gotenberg quickstart.

Zainstaluj mostek, klienta PSR-18 oraz fabryki PSR-17.

Okno terminala
composer require nextpdf/gotenberg guzzlehttp/guzzle

Uruchom usługę Gotenberg dostępną przez HTTPS. Tokeny bearer pobieraj z menedżera sekretów lub ze wstrzykniętej wartości środowiskowej. Mostek nigdy nie odczytuje zmiennych środowiskowych ani nie tworzy klienta HTTP; jedno i drugie dostarczasz samodzielnie.

Metoda GotenbergBridge::convertFile() przyjmuje ścieżkę do pliku na dysku. Sprowadza ścieżkę do postaci kanonicznej, aby zablokować przechodzenie po katalogach, mapuje rozszerzenie pliku na obsługiwany format, sprawdza rozmiar i nazwę pliku oraz wysyła żądanie multipart na adres <apiUrl>/forms/libreoffice/convert. Metoda convertString() działa tak samo dla bajtów, które już masz; używa oryginalnej nazwy pliku, aby można było wykryć rozszerzenie.

Wykrywanie formatu opiera się na rozszerzeniu. Mostek mapuje .docx, .xlsx, .pptx, .odt, .ods oraz .odp na odpowiednie formaty, a wszystko inne odrzuca, zgłaszając ValueError, zanim pojawi się jakikolwiek ruch sieciowy. Obiekt wyniku udostępnia wykryty format źródłowy jako wartość wyliczenia.

Mostek wykonuje jedno synchroniczne wywołanie HTTP, objęte walidacją. Nie ponawia prób, nie kolejkuje, nie buforuje ani nie ogranicza tempa żądań; te mechanizmy należą do aplikacji otaczającej mostek. Traktuj każdą konwersję jak zdalne wywołanie usługi, którą obsługujesz, ale której nie kontrolujesz w obrębie procesu, i projektuj z uwzględnieniem jej opóźnień oraz trybów awarii.

Mostek zgłasza błędy jako typowane wyjątki i nigdy nie zwraca częściowego ani niezweryfikowanego wyniku:

  • Status inny niż 200, nagłówek Content-Type bez application/pdf albo treść, która nie zaczyna się od %PDF, powodują zgłoszenie GotenbergConvertException. Mostek zwraca wynik tylko wtedy, gdy wszystkie trzy kontrole zakończą się powodzeniem.
  • Błąd klienta PSR-18, w tym awaria sieci lub przekroczenie limitu czasu, jest opakowywany w GotenbergConvertException, z oryginalnym wyjątkiem jako przyczyną.
  • Błędy walidacji (adres URL inny niż HTTPS, adres prywatny lub zarezerwowany, zbyt duże dane wejściowe, niebezpieczna nazwa pliku) powodują zgłoszenie RuntimeException, zanim pojawi się jakikolwiek ruch sieciowy.
  • Nierozpoznane rozszerzenie pliku powoduje zgłoszenie ValueError, zanim pojawi się jakikolwiek ruch sieciowy.
// Configuration (final readonly):
new GotenbergConfig(
string $apiUrl, // required, must be HTTPS
int $timeout = 30, // hard transfer timeout, seconds
int $maxFileSize = 52_428_800, // 50 MiB
string $apiKey = '', // #[SensitiveParameter]; Bearer when non-empty
list<string> $pinnedPublicKeys = [], // sha256/<base64>
list<string> $backupPublicKeys = [],
)
GotenbergConfig::fromArray(array $config): self
GotenbergConfig::isValid(): bool
// The bridge:
new GotenbergBridge(
GotenbergConfig $config,
ClientInterface $httpClient, // PSR-18
RequestFactoryInterface $requestFactory, // PSR-17
StreamFactoryInterface $streamFactory, // PSR-17
?LoggerInterface $logger = null, // PSR-3
?HtmlSecurityPolicyInterface $htmlSecurityPolicy = null,
?ResponseFactoryInterface $responseFactory = null, // enables pinned transport
)
GotenbergBridge::isAvailable(): bool
GotenbergBridge::convertFile(string $path): GotenbergConvertResult
GotenbergBridge::convertString(string $bytes, string $originalFilename): GotenbergConvertResult

Obiekt wyniku udostępnia pdfData, wyliczenie sourceFormat, metodę isValid() (zwraca true, gdy treść jest niepusta i zaczyna się od %PDF) oraz size(). Pełny opis pól, mapę kluczy fromArray() oraz reguły wyboru transportu znajdziesz na stronie konfiguracji Gotenberg linkowanej w sekcji Zobacz też.

Skonfiguruj usługę, podłącz mostek, wykonaj sondę i przekonwertuj jeden plik.

convert-quickstart.php
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Gotenberg\GotenbergBridge;
use NextPDF\Gotenberg\GotenbergConfig;
use NextPDF\Gotenberg\GotenbergConvertException;
$config = new GotenbergConfig(
apiUrl: 'https://gotenberg.example.com',
timeout: 60,
apiKey: getenv('GOTENBERG_TOKEN') ?: '',
);
$bridge = new GotenbergBridge(
config: $config,
httpClient: $httpClient, // your PSR-18 client
requestFactory: $requestFactory, // your PSR-17 factory
streamFactory: $streamFactory, // your PSR-17 factory
responseFactory: $responseFactory, // enables the pinned transport
);
// Probe before converting. The probe validates the URL with no network
// traffic, then sends a HEAD to <apiUrl>/health.
if (!$bridge->isAvailable()) {
throw new RuntimeException('Gotenberg is not reachable.');
}
try {
$result = $bridge->convertFile('/path/to/report.docx');
} catch (GotenbergConvertException $exception) {
// Bad config, HTTP failure, non-200, wrong Content-Type, or non-PDF body.
throw $exception;
}
if (!$result->isValid()) {
throw new RuntimeException('Result is not a valid PDF.');
}
file_put_contents('/path/to/report.pdf', $result->pdfData);

To klasa NextPDF\Gotenberg\GotenbergConfig (powyższy wiersz używa dokładnej przestrzeni nazw, którą musi zaimportować kod). Metoda isAvailable() zwraca false i nigdy nie zgłasza wyjątku w przypadku pustego adresu URL, adresu innego niż HTTPS, adresu prywatnego ani żadnego błędu sieci; status poniżej 500 zwrócony z /health oznacza dostępność.

Konwersja produkcyjna przechwytuje każdy typ błędu osobno, ponawia próbę tylko wtedy, gdy spełnione są właściwe warunki, i ogranicza współbieżność po stronie wywołującego. Poniższa kolejność bloków catch jest wyczerpująca.

OfficeConverter.php
<?php
declare(strict_types=1);
use NextPDF\Gotenberg\GotenbergBridge;
use NextPDF\Gotenberg\GotenbergConvertException;
use Psr\Log\LoggerInterface;
use RuntimeException;
use ValueError;
final readonly class OfficeConverter
{
public function __construct(
private GotenbergBridge $bridge,
private LoggerInterface $logger,
) {}
public function convert(string $path): string
{
try {
$result = $this->bridge->convertFile($path);
} catch (GotenbergConvertException $exception) {
// Transport, non-200, wrong Content-Type, or non-PDF body.
// Retry only on transport-level or 502/503/504 causes, with
// bounded exponential backoff and jitter — never blind retries.
$this->logger->error('gotenberg.convert.failed', [
'path' => basename($path),
'exception' => $exception::class,
]);
throw $exception;
} catch (ValueError $exception) {
// Extension is not one of the six recognized Office formats.
$this->logger->warning('gotenberg.convert.unsupported_format', [
'path' => basename($path),
]);
throw $exception;
} catch (RuntimeException $exception) {
// Non-HTTPS URL, private address, oversized input, or unsafe name.
$this->logger->error('gotenberg.convert.rejected', [
'path' => basename($path),
'exception' => $exception::class,
]);
throw $exception;
}
if (!$result->isValid()) {
throw new RuntimeException('Gotenberg returned an invalid PDF body.');
}
return $result->pdfData;
}
}

Ponawiaj próbę tylko przy GotenbergConvertException na poziomie transportu (opakowanym wyjątku klienta PSR-18) oraz dla idempotentnych błędów serwera (502, 503, 504). Odpowiedź z klasy 400 zwykle oznacza, że dane wejściowe są nieprawidłowe, więc ponowienie zakończy się tak samo. Ogranicz łączną liczbę prób oraz całkowity czas rzeczywisty. Ogranicz liczbę równolegle wykonywanych konwersji do przepustowości, którą wytrzymuje wdrożenie Gotenberg. Sam mostek jest bezstanowy i bezpieczny w użyciu z wielu procesów roboczych, ale usługa ma skończoną przepustowość konwersji.

  • Wykrywanie formatu odbywa się na podstawie rozszerzenia. Plik .docx przemianowany na .txt jest odrzucany z błędem ValueError; plik .txt przemianowany na .docx jest wysyłany do Gotenberg i tam kończy się niepowodzeniem. Przy przyjmowaniu przesyłanych plików opieraj się na rzeczywistym formacie, a nie na nazwie.
  • Metoda fromArray() jest z założenia pobłażliwa. Po cichu podstawia wartości domyślne w miejsce nieprawidłowych danych wejściowych. Sprawdzaj tablicę źródłową na ścieżce rozruchu, aby brakujący adres URL ujawnił się wcześnie jako błąd konfiguracji, a nie jako wyjątek przy każdej konwersji.
  • Limit rozmiaru jest egzekwowany w obrębie procesu. Parametr maxFileSize (domyślnie 50 MiB) jest sprawdzany przed wysłaniem żądania, więc zbyt duży plik nigdy nie zużywa przepustowości usługi. Obniż limit, aby odpowiadał potrzebom Twoich dokumentów; mniejszy limit to tańszy mechanizm ochrony przed odmową usługi.
  • Sonda nie jest bezkosztowa. Wywołuj isAvailable() z punktu końcowego gotowości lub kontroli stanu, a nie przed każdą konwersją. Uruchamianie jej przy każdej konwersji podwaja liczbę żądań kierowanych do usługi bez żadnej korzyści.
  • Brak buforowania w obrębie procesu. Jeśli ten sam dokument jest konwertowany wielokrotnie, buforuj powstały plik PDF w aplikacji, kluczując go skrótem treści danych wejściowych.
  • Pole renderTimeMs ustawiasz samodzielnie. Pole czasu w wyniku wynosi 0.0, chyba że integracja je zmierzy i ustawi. Zmierz czas wywołania samodzielnie, jeśli potrzebujesz tej wartości.

Przez cały czas trwania żądania zajęte są jedno połączenie i jeden proces roboczy LibreOffice po stronie Gotenberg, a sama konwersja Office wymaga czasu. Ustaw timeout na podstawie zmierzonego opóźnienia konwersji dla rzeczywistych dokumentów, z zapasem. Utrzymuj go poniżej limitu dowolnej nadrzędnej bramy lub PHP max_execution_time, aby mostek przekroczył limit czasu jako pierwszy i abyś otrzymał typowany wyjątek zamiast wymuszonego zakończenia procesu. Ogranicz współbieżność za pomocą kolejki, semafora lub puli procesów roboczych dobranej do przepustowości usługi. Mostek nie ma bufora w obrębie procesu; dodaj go w aplikacji, jeśli wielokrotnie konwertujesz te same dane wejściowe.

  • HTTPS i weryfikacja adresu przed wysłaniem. Mostek odrzuca adres URL inny niż HTTPS oraz miejsce docelowe, które rozwiązuje się na prywatną lub zarezerwowaną przestrzeń adresową, zanim z procesu wyjdzie jakiekolwiek żądanie. Każde ponowne wywołanie ponownie wykonuje tę walidację, więc ponowienie nie może obejść zabezpieczenia przed SSRF.
  • Transport z przypinaniem na żądanie. Gdy dostarczysz fabrykę odpowiedzi oraz przypięcia (lub gdy istnieje rozwiązany zbiór adresów IP), mostek wiąże połączenie z rozwiązanymi adresami, wymusza przypinanie SPKI, weryfikuje peera i hosta, stosuje limit czasu oraz wyłącza podążanie za przekierowaniami. Skonfiguruj zapasowe przypięcie przed rotacją certyfikatu.
  • Nie ufaj deklarowanemu typowi treści przesyłanego pliku. Przyjmując pliki przesyłane przez użytkowników, sam zweryfikuj rzeczywisty typ pliku; mapa rozszerzenie–format to decyzja o routingu, a nie kontrola autentyczności.
  • Sekrety są maskowane i niezmienne. Parametr apiKey ma atrybut #[SensitiveParameter], a konfiguracja jest final readonly. Pobieraj token z menedżera sekretów; nigdy nie umieszczaj go w repozytorium. Zapisany w dzienniku wpis konwersji zawiera adres URL, nazwę pliku, format oraz długość treści — nigdy zawartość pliku ani tokenu.
  • Nigdy nie pisz pustego bloku catch. Każdy przykład przechwytuje konkretny typ i zapisuje w dzienniku wraz z kontekstem.

Pełny model bezpieczeństwa i wdrożenia znajdziesz na stronie bezpieczeństwa i operacji Gotenberg. Kontrakt transportu PSR-18 oraz wskazówka, by nie ufać typowi treści, są powiązane z odpowiednimi klauzulami na nadrzędnej stronie użycia produkcyjnego.

Ten przewodnik nie formułuje własnych normatywnych deklaracji zgodności ze standardami. Zachowanie transportu PSR-18 w mostku (klient zgłasza wyjątek tylko wtedy, gdy nie może wysłać żądania ani sparsować odpowiedzi; odpowiedź 4xx/5xx to normalna wartość zwracana), wskazówki dotyczące walidacji przesyłanych plików oraz model przypinania TLS są przypisane do PSR-18, OWASP i RFC 7469 na nadrzędnych stronach użycia produkcyjnego i konfiguracji Gotenberg. Ta strona poradnika powtarza sposób użycia i odsyła po te cytowania do tamtych stron. Mostek wytwarza bajty pliku PDF i na tym kończy. Podpisywanie, profile PDF/A oraz znaki wodne należą do zadań przetwarzania końcowego w NextPDF oraz funkcji edycji komercyjnej, a nie do tego mostka.