コンテンツにスキップ

Gotenberg で Office 文書を PDF に変換する

Gotenberg ブリッジは Office 文書を PDF に変換します。文書を HTTPS 経由で Gotenberg マイクロサービスへ送信し、PDF のバイト列を返します。イミュータブルな GotenbergConfig でサービスを記述し、PSR-18 クライアントと PSR-17 ファクトリーを GotenbergBridge に組み込みます。さらに、ヘルスチェックでサービスをプローブしてから、ディスク上のファイルまたはメモリ内のバイト列を変換します。このガイドでは、ファイル拡張子によるフォーマット検出、ヘルスプローブ、型付きの失敗コントラクト、NextPDF の後処理への引き渡しを扱います。

前提条件は次のとおりです。

  • NextPDF コアと nextpdf/gotenberg がインストールされていること。
  • Gotenberg サービスに HTTPS 経由で到達できること。ブリッジは、リクエストがプロセス外へ出る前に、素の http:// URL を拒否します。
  • PSR-18 クライアントと、PSR-17 のリクエストファクトリーおよびストリームファクトリーがインストールされていること。DNS と TLS のピンニングを使う場合は、PSR-17 レスポンスファクトリーも指定します。
  • 入力が、認識される 6 つの Office フォーマットのいずれかであること。.docx.xlsx.pptx.odt.ods、または .odp です。ブリッジは、それ以外の拡張子を ValueError で拒否します。

このページは how-to ガイドです。実行可能な完全なプログラムについては、Gotenberg クイックスタートを参照してください。

ブリッジ、PSR-18 クライアント、PSR-17 ファクトリーをインストールします。

Terminal window
composer require nextpdf/gotenberg guzzlehttp/guzzle

HTTPS 経由で到達可能な Gotenberg サービスを実行し、ベアラートークンはシークレットマネージャーまたは注入された環境値から取得します。ブリッジは環境変数を読み取らず、HTTP クライアントも構築しません。どちらも呼び出し側で用意してください。

GotenbergBridge::convertFile() はディスク上のパスを受け取ります。トラバーサルを防ぐためにパスを正規化し、ファイル拡張子をサポートされるフォーマットにマッピングし、サイズとファイル名を検査してから、<apiUrl>/forms/libreoffice/convert にマルチパートリクエストを送信します。convertString() は、すでに保持しているバイト列に対して同じ処理を行います。拡張子を検出できるよう、元のファイル名を使用します。

フォーマットは拡張子で検出されます。ブリッジは .docx.xlsx.pptx.odt.ods.odp をそれぞれのフォーマットにマッピングし、それ以外はネットワーク通信が発生する前に ValueError で拒否します。結果オブジェクトは、検出されたソースフォーマットを enum 値として公開します。

ブリッジの処理は、検証を伴う単一の同期 HTTP ラウンドトリップです。リトライ、キューイング、キャッシュ、レート制限は行いません。これらはブリッジを取り巻くアプリケーション側の責務です。各変換は、自分で運用していてもプロセス内では制御できないサービスへのリモート呼び出しとして扱い、その遅延と失敗を見込んで設計してください。

ブリッジは失敗を型付き例外として明示し、部分的な結果や未検証の結果を返すことはありません。

  • ステータスコードが 200 以外、Content-Typeapplication/pdf を含まない、またはボディが %PDF で始まらない場合は、いずれも GotenbergConvertException を送出します。ブリッジは、3 つのチェックすべてに合格した場合にのみ結果を返します。
  • ネットワーク障害やタイムアウトを含む PSR-18 クライアントの失敗は、元の例外を原因として GotenbergConvertException にラップされます。
  • 検証の失敗(非 HTTPS の URL、プライベートまたは予約済みアドレス、サイズ超過の入力、安全でないファイル名)では、ネットワーク通信が発生する前に RuntimeException を送出します。
  • 認識されないファイル拡張子では、ネットワーク通信が発生する前に ValueError を送出します。
// 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

結果オブジェクトは、pdfDatasourceFormat enum、isValid()(ボディが空でなく %PDF で始まる場合に true)、および size() を公開します。フィールドの完全なリファレンス、fromArray() のキーマップ、トランスポート選択のルールについては、「関連項目」からリンクしている Gotenberg 設定ページを参照してください。

サービス設定を記述し、ブリッジを組み込み、プローブしてから、1 つのファイルを変換します。

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);

クラスは NextPDF\Gotenberg\GotenbergConfig です(上の行では、コードでインポートすべき正確な名前空間を使用しています)。isAvailable() は、空、非 HTTPS、プライベートアドレスの URL、およびあらゆるネットワークエラーに対して false を返し、例外を送出することはありません。500 未満のステータスが /health から返る場合は、利用可能であることを意味します。

本番環境の変換では、各失敗の種類を個別にキャッチし、適切な条件下でのみリトライし、呼び出し側で並行数を制限します。以下の catch の順序は網羅的です。

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;
}
}

リトライは、トランスポートレベルの GotenbergConvertException(ラップされた PSR-18 クライアント例外)と、冪等なサーバーエラー(502503504)の場合にのみ行ってください。400 系のレスポンスは、通常は入力が誤っていることを意味するため、リトライしても同様に失敗します。試行回数の合計と総実時間の上限を設けてください。実行中の変換の数は、Gotenberg のデプロイが維持できる処理能力に合わせて制限してください。ブリッジ自体はステートレスで、多数のワーカーから安全に使用できますが、サービスの変換能力には限りがあります。

  • フォーマットは拡張子で検出されます。 .docx.txt にリネームすると ValueError で拒否され、.txt.docx にリネームすると Gotenberg に送信され、そこで失敗します。アップロードを受け付ける際は、名前ではなく実際のフォーマットを信頼対象にしてください。
  • fromArray() は意図的に寛容です。 不正な入力は、黙ってデフォルト値に置き換えます。URL の欠落が、変換ごとの例外としてではなく設定エラーとして早期に表面化するよう、ブートパスでソース配列を検証してください。
  • サイズの上限はプロセス内で強制されます。 maxFileSize(デフォルト 50 MiB)はリクエストが送信される前にチェックされるため、サイズ超過のファイルがサービスの処理能力を消費することはありません。対象ドキュメントに必要な範囲に合わせて上限を引き下げてください。上限が小さいほど、より安価なサービス拒否対策になります。
  • プローブは無料ではありません。 isAvailable() は、変換のたびにではなく、レディネスエンドポイントまたはヘルスエンドポイントから呼び出してください。変換ごとに実行すると、利点がないままサービスへのリクエストレートが 2 倍になります。
  • プロセス内キャッシュはありません。 同じドキュメントを繰り返し変換する場合は、入力のコンテンツハッシュをキーとして、結果の PDF をアプリケーション側でキャッシュしてください。
  • renderTimeMs は自分で設定するものです。 結果のタイミングフィールドは、統合側が計測して設定しない限り 0.0 です。その数値が必要な場合は、呼び出し時間を自分で計測してください。

リクエスト中、1 回の変換は Gotenberg 側で 1 つの接続と 1 つの LibreOffice ワーカーを占有し、Office 変換は瞬時には完了しません。timeout は、実際のドキュメントで計測した変換レイテンシをもとに、余裕を持たせて設定してください。プロセスが強制終了される前にブリッジが先にタイムアウトし、型付きの例外が得られるよう、上流のゲートウェイや PHP の max_execution_time よりも小さく保ってください。サービスの処理能力に合わせてサイズを調整したキュー、セマフォ、またはワーカープールで並行数を制限してください。プロセス内キャッシュはありません。同じ入力を繰り返し変換する場合は、アプリケーション側でキャッシュを追加してください。

  • 送信前の HTTPS とアドレスの検査。 ブリッジは、リクエストがプロセス外へ出る前に、非 HTTPS の URL と、プライベートまたは予約済みのアドレス空間に resolve(解決)される宛先を拒否します。リトライ時の各呼び出しでもその検証を再実行するため、リトライによって SSRF ガードを回避することはできません。
  • リクエスト時のピン留めされたトランスポート。 レスポンスファクトリーとピンを指定した場合(または解決済みの IP セットがある場合)、ブリッジは接続を解決済みのアドレスにバインドし、SPKI ピンニングを強制し、ピアとホストを検証し、タイムアウトを適用し、リダイレクトの追従を無効にします。証明書のローテーションの前に、バックアップ用のピンを設定してください。
  • アップロードで宣言されたコンテンツタイプを信頼しないでください。 ユーザーのアップロードを受け付ける際は、実際のファイルタイプを自分で検証してください。拡張子からフォーマットへのマッピングはルーティング判断であり、真正性のチェックではありません。
  • シークレットは秘匿化され、イミュータブルです。 apiKey#[SensitiveParameter] を持ち、設定は final readonly です。トークンはシークレットマネージャーから取得し、決してコミットしないでください。変換ログのエントリには、URL、ファイル名、フォーマット、コンテンツ長が含まれますが、ファイルの内容やトークンは決して含まれません。
  • 空の catch ブロックは決して書かないでください。 各例では、特定の型をキャッチし、コンテキスト付きでログを記録します。

セキュリティとデプロイモデルの全体像については、Gotenberg のセキュリティと運用のページを参照してください。PSR-18 のトランスポートコントラクトと、コンテンツタイプを信頼しないというガイダンスは、上流の本番利用ページの該当する条項に紐付けられています。

このガイド自体は、規範的な標準への主張を行いません。ブリッジの PSR-18 トランスポートの挙動(クライアントは、レスポンスを送信またはパースできない場合にのみ例外を送出し、4xx/5xx は通常の戻り値です)、ファイルアップロードの検証ガイダンス、TLS ピンニングのモデルは、上流の Gotenberg 本番利用ページおよび設定ページで、PSR-18、OWASP、RFC 7469 に紐付けられています。このクックブックページは使い方を再掲し、それらの引用は該当ページに委ねます。ブリッジは PDF のバイト列を生成して、そこで終了します。署名、PDF/A プロファイル、透かしは NextPDF の後処理に関する事項であり、商用エディションの機能であって、このブリッジの一部ではありません。