PDF Blocks
PreciosSoporte
Empezar gratis
Ir a la página

Trabajar con archivos grandes

Qué cambiar cuando los PDF se vuelven grandes: streaming, tiempos de espera, reintentos, los límites de tamaño y cuándo conviene dividir antes.

Los PDF grandes son la misma solicitud que los pequeños (entra un archivo, sale un archivo), pero el enfoque ingenuo de leer todo el documento en memoria y esperar con un tiempo de espera predeterminado se cae a medida que los archivos crecen. Transmita la carga y la descarga en streaming, dele a la solicitud margen para terminar y reintente los fallos transitorios. Como la API es stateless, una solicitud que falla no deja nada detrás, así que reintentar es seguro.

Transmita en streaming, no acumule en memoria

Lea la entrada del disco por trozos a medida que la envía, y escriba la respuesta en disco por trozos a medida que llega. Nunca mantenga el documento completo en memoria. Todos los ejemplos que siguen transmiten en ambos sentidos y estampan una marca de agua en big.pdf como sustituto de cualquier acción de salida ú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

Ponga tiempos de espera generosos

Un documento grande tarda más en cargarse y en procesarse, así que un tiempo de espera predeterminado del cliente, a menudo de 30 segundos o menos, cortará una solicitud que iba bien. Reparta el presupuesto:

  • Un tiempo de espera de conexión corto (unos pocos segundos) falla rápido cuando el host no está accesible.
  • Un tiempo de espera de lectura largo le da al procesamiento tiempo para terminar. Los ejemplos anteriores usan diez minutos; dimensiónelo según el documento más grande que espere.

No establezca un tiempo de espera sin límite: conviene que una conexión atascada se rinda en algún momento para que un reintento tome el relevo.

Reintente los fallos transitorios

Los reinicios de red, los tiempos de espera de conexión y (cuando se apliquen) las respuestas 429 o 5xx son transitorios: es muy posible que la misma solicitud tenga éxito un momento después. Reintente con espera exponencial y algo de variación aleatoria. Reintentar aquí es seguro precisamente porque la API es stateless: cada llamada es una función pura de sus entradas, sin escrituras parciales que deshacer.

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

No reintente un 400: un error de validación no se arregla solo, y el cuerpo application/problem+json le dice qué campo corregir. Consulte Errores para ver el catálogo. Desde la línea de comandos, cURL ya lo trae incorporado: --retry 3 --retry-delay 2 --retry-all-errors.

Límites de tamaño y rate limits

Los límites de tamaño de las solicitudes, los rate limits y sus códigos de estado 413, 429 y afines son una parte reservada del contrato de la API y todavía no se aplican. No se publican umbrales fijos: diseñe para el contrato, no para un número concreto.

Para mantener la robustez a medida que los límites entren en vigor, mantenga cada solicitud en un tamaño razonable, trate 413 y 429 como reintentables con espera exponencial (o, en el caso de 413, como una señal para dividir la entrada) y respete la cabecera Retry-After cuando esté presente. Consulte Rate limits y uso para ver cómo se mide el uso.

Divida primero los archivos muy grandes

Si un documento es demasiado grande o demasiado lento para procesarlo en una sola llamada, pártalo en partes más pequeñas, procese cada una por separado y, si necesita un solo archivo de vuelta, una los resultados. Así se acota la memoria y el tiempo que necesita cualquier solicitud individual, y permite procesar las partes en paralelo.

Divida en partes

Use Dividir por tamaño de archivo o Dividir por número de páginas para producir partes manejables. Consulte Dividir un PDF para saber cómo desempaquetar la salida.

Procese cada parte

Ejecute su acción sobre cada parte, en paralelo si quiere, ya que las llamadas son independientes.

Una las partes si hace falta

Vuelva a combinar las partes procesadas con Unir documentos PDF.

Relacionado