PDF Blocks
PreçosSuporte
Começar grátis
Abrir a página

Trabalhar com arquivos grandes

O que mudar quando os PDFs ficam grandes: streaming, tempos limite, novas tentativas, os limites de tamanho e quando dividir antes.

PDFs grandes são a mesma requisição que os pequenos (um arquivo entra, um arquivo sai), mas a abordagem ingênua de ler o documento inteiro na memória e esperar por um tempo limite padrão desmorona à medida que os arquivos crescem. Envie e baixe em fluxo, dê à requisição espaço para terminar e repita as falhas transitórias. Como a API é stateless, uma requisição que falha não deixa nada para trás, então repetir é seguro.

Trabalhe em fluxo, não em buffer

Leia a entrada do disco em blocos enquanto a envia, e grave a resposta no disco em blocos à medida que ela chega. Nunca mantenha o documento inteiro na memória. Todos os exemplos abaixo trabalham em fluxo nos dois sentidos e carimbam uma marca-d’água em big.pdf, que serve de substituto para qualquer ação de saída única.

cURLbash
# curl streams the upload from disk and the response to disk by default.
curl https://api.pdfblocks.com/v1/add_text_watermark \
  -H 'X-API-Key: your_api_key' \
  -F file=@big.pdf \
  -F line_1='CONFIDENTIAL' \
  --max-time 600 \
  --retry 3 --retry-delay 2 --retry-all-errors \
  -o watermarked.pdf
Pythonpython
# pip install requests requests-toolbelt
import requests
from requests_toolbelt.multipart.encoder import MultipartEncoder

with open('big.pdf', 'rb') as file:
    form = MultipartEncoder(fields={
        'file': ('big.pdf', file, 'application/pdf'),  # streamed from disk
        'line_1': 'CONFIDENTIAL',
    })
    with requests.post(
        'https://api.pdfblocks.com/v1/add_text_watermark',
        headers={'X-API-Key': 'your_api_key', 'Content-Type': form.content_type},
        data=form,
        stream=True,
        timeout=(10, 600),  # 10s to connect, 600s to read
    ) as response:
        response.raise_for_status()
        with open('watermarked.pdf', 'wb') as output:
            for chunk in response.iter_content(chunk_size=65536):
                output.write(chunk)
Node.jsjavascript
// npm install form-data
import { createReadStream, createWriteStream } from 'node:fs';
import { request } from 'node:https';
import FormData from 'form-data';

const form = new FormData();
form.append('file', createReadStream('big.pdf')); // streamed from disk
form.append('line_1', 'CONFIDENTIAL');

const req = request(
  'https://api.pdfblocks.com/v1/add_text_watermark',
  {
    method: 'POST',
    headers: { 'X-API-Key': 'your_api_key', ...form.getHeaders() },
    timeout: 600000,
  },
  (response) => {
    if (response.statusCode !== 200) {
      throw new Error(`Request failed: ${response.statusCode}`);
    }
    response.pipe(createWriteStream('watermarked.pdf')); // streamed to disk
  },
);

form.pipe(req); // stream the multipart body
Gogo
package main

import (
	"io"
	"mime/multipart"
	"net/http"
	"os"
	"time"
)

func main() {
	// Generate the multipart body on the fly with io.Pipe: the file is never
	// held in memory in full.
	pr, pw := io.Pipe()
	form := multipart.NewWriter(pw)

	go func() {
		defer pw.Close()
		defer form.Close()
		file, _ := os.Open("big.pdf")
		defer file.Close()
		part, _ := form.CreateFormFile("file", "big.pdf")
		io.Copy(part, file) // streamed, not buffered
		form.WriteField("line_1", "CONFIDENTIAL")
	}()

	req, _ := http.NewRequest("POST",
		"https://api.pdfblocks.com/v1/add_text_watermark", pr)
	req.Header.Set("Content-Type", form.FormDataContentType())
	req.Header.Set("X-API-Key", "your_api_key")

	client := &http.Client{Timeout: 10 * time.Minute}
	res, err := client.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	out, _ := os.Create("watermarked.pdf")
	defer out.Close()
	io.Copy(out, res.Body) // streamed to disk
}
C#csharp
using var client = new HttpClient { Timeout = TimeSpan.FromMinutes(10) };
client.DefaultRequestHeaders.Add("X-API-Key", "your_api_key");

using var fileStream = File.OpenRead("big.pdf");
using var form = new MultipartFormDataContent
{
    { new StreamContent(fileStream), "file", "big.pdf" }, // streamed from disk
    { new StringContent("CONFIDENTIAL"), "line_1" },
};

// ResponseHeadersRead returns as soon as the headers arrive, so the body isn't
// buffered before you read it.
using var response = await client.PostAsync(
    "https://api.pdfblocks.com/v1/add_text_watermark",
    form,
    HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();

await using var output = File.Create("watermarked.pdf");
await response.Content.CopyToAsync(output); // streamed to disk

Defina tempos limite generosos

Um documento grande demora mais para ser enviado e processado, então um tempo limite padrão do cliente, muitas vezes de 30 segundos ou menos, vai interromper uma requisição saudável antes da hora. Divida o orçamento:

  • Um tempo limite curto de conexão (alguns segundos) falha rápido quando o host está inacessível.
  • Um tempo limite longo de leitura dá ao processamento tempo para terminar. Os exemplos acima usam dez minutos; dimensione-o pelo maior documento que você espera receber.

Não defina um tempo limite ilimitado: você ainda quer que uma conexão travada desista em algum momento, para que uma nova tentativa assuma.

Repita após falhas transitórias

Resets de rede, tempos limite de conexão e (quando forem aplicadas) respostas 429 ou 5xx são transitórios: a mesma requisição pode muito bem ter sucesso um instante depois. Repita com backoff exponencial e um pouco de jitter. Repetir é seguro aqui justamente porque a API é stateless: cada chamada é uma função pura das suas entradas, sem escritas parciais a desfazer.

Pythonpython
import random
import time
import requests


def send_with_retries(build_request, attempts=4):
    for attempt in range(attempts):
        try:
            response = build_request()
            # Retry only on server-side / throttling responses.
            if response.status_code < 500 and response.status_code != 429:
                return response
        except requests.RequestException:
            if attempt == attempts - 1:
                raise
        # Back off: 1s, 2s, 4s, … plus jitter.
        time.sleep(2 ** attempt + random.uniform(0, 1))
    return response

Não tente de novo depois de um 400: um erro de validação não vai se corrigir sozinho, e o corpo application/problem+json informa qual campo corrigir. Consulte Erros para o catálogo. Na linha de comando, o cURL já traz isso embutido: --retry 3 --retry-delay 2 --retry-all-errors.

Limites de tamanho e rate limits

Os limites de tamanho de requisição, os rate limits e seus códigos de status 413, 429 e relacionados são uma parte reservada do contrato da API e ainda não são aplicados. Nenhum limiar fixo foi publicado. Projete para o contrato, e não para um número específico.

Para continuar robusto conforme os limites entrarem em vigor, mantenha cada requisição em um tamanho razoável, trate 413 e 429 como casos a repetir com backoff (ou, no caso do 413, como um sinal para dividir a entrada) e respeite um cabeçalho Retry-After quando ele estiver presente. Consulte Rate limits e uso para saber como o uso é medido.

Divida primeiro os arquivos muito grandes

Se um único documento for grande ou lento demais para ser processado em uma chamada, divida-o em partes menores, processe cada uma de forma independente e (se você precisar de um arquivo só de volta) mescle os resultados. Isso limita a memória e o tempo que qualquer requisição isolada exige, e permite processar as partes em paralelo.

Divida em partes

Use Dividir por tamanho de arquivo ou Dividir por número de páginas para produzir blocos gerenciáveis. Consulte Dividir um PDF para saber como descompactar a saída.

Processe cada parte

Execute a sua ação em cada parte, em paralelo se quiser, já que as chamadas são independentes.

Mescle se precisar

Recombine as partes processadas com Mesclar documentos PDF.

Veja também