Converta documentos do Office em PDF com o Gotenberg
Visão geral
Seção intitulada “Visão geral”A ponte Gotenberg converte documentos do Office em PDF. Ela envia o documento para um microsserviço Gotenberg por HTTPS e retorna os bytes do PDF. Você descreve o serviço com um GotenbergConfig imutável, injeta um cliente PSR-18 e fábricas PSR-17 no GotenbergBridge, verifica a integridade do serviço e converte um arquivo do disco ou bytes em memória. Este guia aborda a detecção de formato por extensão, a verificação de integridade, o contrato de falhas tipadas e a passagem para o pós-processamento do NextPDF.
Pré-requisitos, declarados de antemão:
- O NextPDF core e o
nextpdf/gotenbergestão instalados. - Um serviço Gotenberg está acessível por HTTPS. A ponte rejeita uma URL
http://sem TLS antes que qualquer requisição saia do processo. - Um cliente PSR-18 e fábricas de requisição e de stream PSR-17 estão instalados. Para pinning de DNS e TLS, você também fornece uma fábrica de resposta PSR-17.
- A entrada é um dos seis formatos do Office reconhecidos:
.docx,.xlsx,.pptx,.odt,.odsou.odp. A ponte rejeita qualquer outra extensão com umValueError.
Este é um guia prático. Para um programa completo e executável, leia o guia de início rápido do Gotenberg.
Instalação
Seção intitulada “Instalação”Instale a ponte, um cliente PSR-18 e fábricas PSR-17.
composer require nextpdf/gotenberg guzzlehttp/guzzleExecute um serviço Gotenberg acessível por HTTPS. Obtenha qualquer token bearer necessário de um gerenciador de segredos ou de um valor de ambiente injetado. A ponte nunca lê variáveis de ambiente e nunca constrói um cliente HTTP; você fornece esses valores e dependências.
Visão conceitual
Seção intitulada “Visão conceitual”GotenbergBridge::convertFile() recebe um caminho no disco. Ela canonicaliza o caminho para bloquear path traversal, mapeia a extensão do arquivo para um formato suportado, verifica o tamanho e o nome do arquivo e envia uma requisição multipart para <apiUrl>/forms/libreoffice/convert. convertString() segue o mesmo fluxo para bytes que você já possui; ele usa o nome de arquivo original para que a extensão possa ser detectada.
A detecção de formato usa a extensão. A ponte mapeia .docx, .xlsx, .pptx, .odt, .ods e .odp para seus formatos e rejeita qualquer outra entrada com um ValueError antes de qualquer tráfego de rede. O objeto de resultado expõe o formato de origem detectado como um valor de enum.
A ponte faz uma única chamada HTTP síncrona de ida e volta, cercada por validação. Ela não faz novas tentativas, enfileiramento, cache nem limitação de taxa; esses controles ficam na aplicação que envolve a ponte. Trate cada conversão como uma chamada remota a um serviço que você opera, mas não controla no mesmo processo, e projete considerando a latência e os modos de falha dele.
A ponte relata falhas como exceções tipadas e nunca retorna um resultado parcial ou não validado:
- Um status diferente de
200, umContent-Typesemapplication/pdfou um corpo que não começa com%PDFlançaGotenbergConvertException. A ponte retorna um resultado somente quando as três verificações passam. - Uma falha do cliente PSR-18, incluindo falha de rede ou timeout, é envolvida como
GotenbergConvertExceptioncom a exceção original como causa. - Falhas de validação (URL não HTTPS, endereço privado ou reservado, entrada de tamanho excessivo, nome de arquivo inseguro) lançam
RuntimeExceptionantes de qualquer tráfego de rede. - Uma extensão de arquivo não reconhecida lança
ValueErrorantes de qualquer tráfego de rede.
Superfície da API
Seção intitulada “Superfície da API”// 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): selfGotenbergConfig::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(): boolGotenbergBridge::convertFile(string $path): GotenbergConvertResultGotenbergBridge::convertString(string $bytes, string $originalFilename): GotenbergConvertResultO objeto de resultado expõe pdfData, o enum sourceFormat, isValid() (true quando o corpo não está vazio e começa com %PDF) e size(). Para ver a referência completa de campos, o mapa de chaves de fromArray() e as regras de seleção de transporte, consulte a página de configuração do Gotenberg vinculada em Veja também.
Exemplo de código — Início rápido
Seção intitulada “Exemplo de código — Início rápido”Descreva o serviço, injete a ponte, verifique sua disponibilidade e converta um arquivo.
<?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);A classe é NextPDF\Gotenberg\GotenbergConfig (a linha acima usa o namespace exato que o código deve importar). isAvailable() retorna false e nunca lança exceção para URL vazia, não HTTPS, de endereço privado ou para qualquer erro de rede; um status abaixo de 500 em /health significa disponível.
Exemplo de código — Produção
Seção intitulada “Exemplo de código — Produção”Uma conversão de produção captura cada tipo de falha separadamente, tenta novamente somente quando as condições estão certas e limita a concorrência do lado do chamador. A ordem dos blocos catch abaixo é exaustiva.
<?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; }}Tente novamente somente diante de uma GotenbergConvertException de nível de transporte (uma exceção de cliente PSR-18 envolvida) e de erros de servidor idempotentes (502, 503, 504). Uma resposta da classe 400 normalmente significa que a entrada está errada, então uma nova tentativa falhará da mesma forma. Limite o total de tentativas e o tempo total decorrido. Limite o número de conversões em andamento à capacidade que a implantação do Gotenberg sustenta. A ponte em si não mantém estado e é segura para uso por muitos workers, mas o serviço tem capacidade de conversão finita.
Casos extremos e pontos de atenção
Seção intitulada “Casos extremos e pontos de atenção”- A detecção de formato é por extensão. Um
.docxrenomeado para.txté rejeitado comValueError; um.txtrenomeado para.docxé enviado ao Gotenberg e falha lá. Ao aceitar uploads, baseie-se no formato real, não no nome. fromArray()é tolerante por design. Ele substitui silenciosamente entradas malformadas por valores padrão. Valide o array de origem no fluxo de inicialização para que uma URL ausente apareça cedo como erro de configuração, e não como exceção por conversão.- O limite de tamanho é aplicado no próprio processo.
maxFileSize(padrão de 50 MiB) é verificado antes de a requisição ser enviada, então um arquivo de tamanho excessivo nunca consome capacidade do serviço. Reduza o limite para corresponder ao tamanho de que os documentos precisam; um limite menor é um controle mais barato contra negação de serviço. - A verificação não é gratuita. Chame
isAvailable()a partir de um endpoint de prontidão ou de integridade, não antes de cada conversão. Executá-la a cada conversão dobra a taxa de requisições contra o serviço sem nenhum benefício. - Não há cache no próprio processo. Se o mesmo documento for convertido repetidamente, armazene o PDF resultante em cache na aplicação, indexado por um hash de conteúdo da entrada.
renderTimeMsé definido por você. O campo de tempo do resultado é0.0a menos que a integração o meça e o defina. Meça você mesmo o tempo da chamada se precisar do número.
Desempenho
Seção intitulada “Desempenho”Durante toda a requisição, uma conversão mantém uma conexão e um worker do LibreOffice no lado do Gotenberg, e a conversão do Office leva tempo. Defina timeout com base na latência de conversão medida para os documentos reais, com folga. Mantenha-o abaixo de qualquer gateway upstream ou do max_execution_time do PHP, para que a ponte atinja o timeout primeiro e você receba uma exceção tipada em vez de ter o processo encerrado. Limite a concorrência com uma fila, um semáforo ou um pool de workers dimensionado para a capacidade do serviço. Não há cache no próprio processo; adicione um na aplicação se você converter a mesma entrada repetidamente.
Notas de segurança
Seção intitulada “Notas de segurança”- Validação de HTTPS e de endereço antes do envio. A ponte rejeita uma URL não HTTPS e um destino que resolve para espaço de endereço privado ou reservado antes que qualquer requisição saia do processo. Cada nova chamada executa essa validação novamente, então uma nova tentativa não pode contornar a proteção contra SSRF.
- Transporte com pinning sob demanda. Quando você fornece uma fábrica de resposta e pins (ou há um conjunto de IPs resolvidos), a ponte vincula a conexão aos endereços resolvidos, aplica o pinning de SPKI, verifica o peer e o host, aplica o timeout e desativa o seguimento de redirecionamentos. Configure um pin de backup antes de uma rotação de certificado.
- Não confie no content type declarado de um upload. Ao aceitar uploads de usuários, valide você mesmo o tipo real do arquivo; o mapeamento de extensão para formato é uma decisão de roteamento, não uma verificação de autenticidade.
- Segredos ficam ocultos e imutáveis.
apiKeycarrega#[SensitiveParameter], e a configuração éfinal readonly. Obtenha o token de um gerenciador de segredos; nunca o inclua em commit. A entrada de conversão registrada em log inclui a URL, o nome do arquivo, o formato e o tamanho do conteúdo — nunca o conteúdo do arquivo ou o token. - Nunca escreva um bloco
catchvazio. Cada exemplo captura o tipo específico e registra em log com contexto.
Para o modelo completo de segurança e implantação, consulte a página de segurança e operações do Gotenberg. O contrato de transporte PSR-18 e a orientação de não confiar no content type estão vinculados às respectivas cláusulas na página de uso em produção upstream.
Conformidade
Seção intitulada “Conformidade”Este guia não faz, por si só, nenhuma alegação normativa de conformidade com padrões. O comportamento de transporte PSR-18 da ponte (um cliente lança exceção apenas quando não consegue enviar ou analisar uma resposta; um 4xx/5xx é um valor de retorno normal), a orientação de validação de upload de arquivos e o modelo de TLS-pinning estão vinculados a PSR-18, OWASP e RFC 7469 nas páginas upstream de uso em produção e de configuração do Gotenberg. Esta página do cookbook reafirma o uso e delega essas citações a essas páginas. A ponte produz bytes de PDF e encerra sua responsabilidade aí. Assinatura, perfis PDF/A e marca d’água são responsabilidades de pós-processamento do NextPDF e recursos da edição comercial; não fazem parte desta ponte.
Veja também
Seção intitulada “Veja também”- Retorne um PDF gerado a partir de um controller — retorne um PDF convertido como resposta HTTP.
- Início rápido do Gotenberg — o programa de conversão completo e executável.
- Configuração do Gotenberg — todos os campos, o mapa de
fromArray()e a seleção de transporte. - Uso em produção do Gotenberg — segredos, timeouts, novas tentativas, concorrência e o limite de pós-processamento.