Lewati ke konten

Mengonversi dokumen Office ke PDF dengan Gotenberg

Jembatan Gotenberg mengonversi dokumen Office ke PDF. Jembatan ini mengirim dokumen ke microservice Gotenberg melalui HTTPS, lalu mengembalikan byte PDF. Anda mendeskripsikan layanan dengan GotenbergConfig yang imutabel, menghubungkan klien PSR-18 dan factory PSR-17 ke GotenbergBridge, memeriksa kesehatan layanan, lalu mengonversi berkas dari disk atau byte di memori. Panduan ini membahas deteksi format berbasis ekstensi, probe kesehatan, kontrak kegagalan bertipe, dan penyerahan hasil ke pasca-pemrosesan NextPDF.

Prasyaratnya dinyatakan sejak awal:

  • Inti NextPDF dan nextpdf/gotenberg sudah terpasang.
  • Layanan Gotenberg dapat dijangkau melalui HTTPS. Jembatan menolak URL http:// biasa sebelum permintaan apa pun meninggalkan proses.
  • Klien PSR-18 serta factory request dan stream PSR-17 sudah terpasang. Untuk penyematan DNS dan TLS, Anda juga perlu menyediakan response factory PSR-17.
  • Input adalah salah satu dari enam format Office yang dikenali: .docx, .xlsx, .pptx, .odt, .ods, atau .odp. Jembatan menolak ekstensi apa pun selain itu dengan ValueError.

Ini adalah panduan praktis. Untuk program lengkap yang dapat dijalankan, lihat panduan ringkas Gotenberg.

Pasang jembatan, klien PSR-18, dan factory PSR-17.

Terminal window
composer require nextpdf/gotenberg guzzlehttp/guzzle

Jalankan layanan Gotenberg yang dapat dijangkau melalui HTTPS. Ambil token bearer dari secrets manager atau nilai environment yang disuntikkan. Jembatan tidak pernah membaca variabel environment dan tidak pernah membuat klien HTTP; Andalah yang menyediakan keduanya.

GotenbergBridge::convertFile() menerima path di disk. Jembatan melakukan kanonisasi path untuk memblokir traversal, memetakan ekstensi berkas ke format yang didukung, memeriksa ukuran dan nama berkas, lalu mengirim permintaan multipart ke <apiUrl>/forms/libreoffice/convert. convertString() mengikuti alur yang sama untuk byte yang sudah Anda miliki; metode ini menggunakan nama berkas asli agar ekstensi dapat dideteksi.

Deteksi format didasarkan pada ekstensi. Jembatan memetakan .docx, .xlsx, .pptx, .odt, .ods, dan .odp ke formatnya masing-masing dan menolak yang lain dengan ValueError sebelum ada lalu lintas jaringan. Objek hasil mengekspos format sumber yang terdeteksi sebagai nilai enum.

Jembatan melakukan satu siklus request-response HTTP sinkron yang dibungkus dengan validasi. Jembatan tidak melakukan coba ulang, antrean, cache, atau pembatasan laju; kontrol tersebut menjadi tanggung jawab aplikasi di sekitar jembatan. Perlakukan setiap konversi sebagai panggilan jarak jauh ke layanan yang Anda operasikan tetapi tidak Anda kendalikan di dalam proses, lalu rancang aplikasi untuk latensi serta mode kegagalannya.

Jembatan melaporkan kegagalan sebagai exception bertipe dan tidak pernah mengembalikan hasil yang parsial atau belum tervalidasi:

  • Status non-200, Content-Type tanpa application/pdf, atau body yang tidak diawali dengan %PDF akan memunculkan GotenbergConvertException. Jembatan hanya mengembalikan hasil ketika ketiga pemeriksaan lolos.
  • Kegagalan klien PSR-18, termasuk kegagalan jaringan atau timeout, dibungkus sebagai GotenbergConvertException dengan exception asli sebagai penyebabnya.
  • Kegagalan validasi (URL non-HTTPS, alamat privat atau dicadangkan, input berukuran berlebih, nama berkas tidak aman) memunculkan RuntimeException sebelum ada lalu lintas jaringan.
  • Ekstensi berkas yang tidak dikenali memunculkan ValueError sebelum ada lalu lintas jaringan.
// 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

Objek hasil mengekspos pdfData, enum sourceFormat, isValid() (true ketika body tidak kosong dan diawali dengan %PDF), dan size(). Untuk referensi field lengkap, peta kunci fromArray(), dan aturan pemilihan transport, lihat halaman konfigurasi Gotenberg yang ditautkan di bagian Lihat juga.

Deskripsikan layanan, hubungkan jembatan, lakukan probe layanan, lalu konversi satu berkas.

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

Kelas yang digunakan adalah NextPDF\Gotenberg\GotenbergConfig (baris di atas menggunakan namespace persis yang harus diimpor oleh kode Anda). isAvailable() mengembalikan false dan tidak pernah melempar exception untuk URL yang kosong, non-HTTPS, atau beralamat privat, maupun untuk kesalahan jaringan apa pun; status di bawah 500 dari /health berarti layanan tersedia.

Konversi produksi menangkap setiap jenis kegagalan secara terpisah, melakukan coba ulang hanya ketika kondisinya tepat, dan membatasi konkurensi di sisi pemanggil. Urutan catch di bawah ini mencakup seluruh kasus.

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

Lakukan coba ulang hanya pada GotenbergConvertException tingkat transport (exception klien PSR-18 yang dibungkus) dan pada kesalahan server yang idempoten (502, 503, 504). Respons kelas 400 biasanya berarti input salah, sehingga coba ulang akan gagal dengan cara yang sama. Batasi total percobaan dan total waktu aktual. Batasi jumlah konversi yang sedang berjalan sesuai kapasitas yang dapat ditopang oleh deployment Gotenberg Anda. Jembatan itu sendiri bersifat stateless dan aman digunakan dari banyak worker, tetapi layanan memiliki kapasitas konversi yang terbatas.

  • Deteksi format berdasarkan ekstensi. Berkas .docx yang diganti namanya menjadi .txt ditolak dengan ValueError; berkas .txt yang diganti namanya menjadi .docx dikirim ke Gotenberg dan gagal di sana. Saat menerima unggahan, percayai format yang sebenarnya, bukan sekadar namanya.
  • fromArray() dirancang untuk bersikap toleran. Metode ini secara diam-diam mengganti input yang cacat dengan nilai standar. Validasi array sumber di jalur boot Anda agar URL yang hilang muncul lebih awal sebagai kesalahan konfigurasi, bukan sebagai exception pada setiap konversi.
  • Batas ukuran diberlakukan dalam proses. maxFileSize (standar 50 MiB) diperiksa sebelum permintaan dikirim, sehingga berkas berukuran berlebih tidak pernah menghabiskan kapasitas layanan. Turunkan batas agar sesuai dengan kebutuhan dokumen Anda; batas yang lebih kecil adalah kontrol denial-of-service yang lebih murah.
  • Probe tidak gratis. Panggil isAvailable() dari endpoint readiness atau kesehatan, bukan sebelum setiap konversi. Menjalankannya pada setiap konversi melipatgandakan laju permintaan Anda ke layanan tanpa manfaat apa pun.
  • Tidak ada cache dalam proses. Jika dokumen yang sama dikonversi berulang kali, cache PDF hasilnya di aplikasi Anda, dengan kunci berupa hash konten dari input.
  • renderTimeMs adalah milik Anda untuk diatur. Field waktu pada hasil bernilai 0.0 kecuali integrasi Anda mengukur dan menetapkannya. Ukur sendiri durasi panggilan jika Anda membutuhkan angka tersebut.

Selama permintaan berlangsung, satu konversi menahan satu koneksi dan satu worker LibreOffice di sisi Gotenberg, dan konversi Office memang membutuhkan waktu. Atur timeout berdasarkan latensi konversi terukur untuk dokumen nyata Anda, dengan margin cadangan. Jaga agar nilainya di bawah gateway upstream mana pun atau max_execution_time PHP, sehingga jembatan mengalami timeout lebih dahulu dan Anda mendapatkan exception bertipe alih-alih proses yang dihentikan paksa. Batasi konkurensi dengan antrean, semaphore, atau worker pool yang diukur sesuai kapasitas layanan. Tidak ada cache dalam proses; tambahkan cache di aplikasi Anda jika Anda mengonversi input yang sama berulang kali.

  • Penyaringan HTTPS dan alamat sebelum pengiriman. Jembatan menolak URL non-HTTPS dan tujuan yang teresolusi ke ruang alamat privat atau dicadangkan sebelum permintaan apa pun meninggalkan proses. Setiap panggilan yang dicoba ulang menjalankan kembali validasi tersebut, sehingga coba ulang tidak dapat melewati penjaga SSRF.
  • Transport tersemat sesuai permintaan. Ketika Anda menyediakan response factory dan pin (atau terdapat kumpulan IP yang teresolusi), jembatan mengikat koneksi ke alamat yang teresolusi, memberlakukan penyematan SPKI, memverifikasi peer dan host, menerapkan timeout, dan menonaktifkan pengikutan pengalihan. Konfigurasikan pin cadangan sebelum rotasi sertifikat.
  • Jangan percayai jenis konten yang dideklarasikan dari sebuah unggahan. Saat menerima unggahan pengguna, validasi sendiri jenis berkas yang sebenarnya; peta ekstensi-ke-format adalah keputusan perutean, bukan pemeriksaan keaslian.
  • Rahasia diredaksi dan imutabel. apiKey membawa #[SensitiveParameter], dan konfigurasinya bersifat final readonly. Ambil token dari secrets manager; jangan pernah meng-commit-nya. Entri konversi yang dicatat membawa URL, nama berkas, format, dan panjang konten — tidak pernah isi berkas atau token.
  • Jangan pernah menulis blok catch yang kosong. Setiap contoh menangkap jenis yang spesifik dan mencatat log dengan konteks.

Untuk model keamanan dan deployment lengkap, lihat halaman keamanan dan operasi Gotenberg. Kontrak transport PSR-18 dan panduan jangan-percayai-content-type disematkan ke klausulnya masing-masing pada halaman penggunaan produksi upstream.

Panduan ini tidak membuat klaim standar normatifnya sendiri. Perilaku transport PSR-18 jembatan (klien hanya melempar exception ketika tidak dapat mengirim atau mengurai respons; 4xx/5xx adalah nilai kembalian yang normal), panduan validasi unggahan berkas, dan model penyematan TLS disematkan ke PSR-18, OWASP, dan RFC 7469 pada halaman penggunaan produksi dan konfigurasi Gotenberg upstream. Halaman cookbook ini menyatakan kembali penggunaannya dan menyerahkan kutipan tersebut ke halaman-halaman itu. Jembatan menghasilkan byte PDF lalu berhenti. Penandatanganan, profil PDF/A, dan penambahan tanda air adalah urusan pasca-pemrosesan NextPDF serta kapabilitas edisi komersial, bukan bagian dari jembatan ini.