PDF Blocks
TarifsSupport
Commencer gratuitement
Ouvrir la page

Travailler avec de gros fichiers

Ce qu’il faut changer une fois que les PDF deviennent volumineux : le streaming, les délais d’attente, les nouvelles tentatives, les limites de taille et le moment où il vaut mieux diviser d’abord.

Les gros PDF donnent lieu à la même requête que les petits (un fichier en entrée, un fichier en sortie), mais l’approche naïve qui consiste à charger tout le document en mémoire et à s’en remettre au délai d’attente par défaut s’écroule à mesure que les fichiers grossissent. Envoyez et téléchargez en flux, laissez à la requête le temps d’aboutir et réessayez après les échecs transitoires. Comme l’API est stateless, une requête qui échoue ne laisse rien derrière elle, donc les nouvelles tentatives sont sans risque.

Travailler en flux, pas en mémoire tampon

Lisez l’entrée depuis le disque par blocs au fur et à mesure de l’envoi, et écrivez la réponse sur le disque par blocs à mesure qu’elle arrive. Ne gardez jamais tout le document en mémoire. Chaque exemple ci-dessous fonctionne en flux dans les deux sens et appose un filigrane sur big.pdf, qui tient lieu de n’importe quelle action à sortie unique.

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

Définir des délais d’attente généreux

Un gros document met plus de temps à être envoyé et à être traité, si bien qu’un délai d’attente client par défaut, souvent de 30 secondes ou moins, interrompra une requête pourtant saine. Répartissez le budget :

  • Un court délai de connexion (quelques secondes) échoue vite lorsque l’hôte est injoignable.
  • Un long délai de lecture laisse au traitement le temps de se terminer. Les exemples ci-dessus utilisent dix minutes ; dimensionnez-le en fonction du plus gros document que vous attendez.

Ne définissez pas de délai illimité : vous voulez tout de même qu’une connexion bloquée finisse par abandonner, pour qu’une nouvelle tentative prenne le relais.

Réessayer après les échecs transitoires

Les réinitialisations réseau, les délais de connexion dépassés et (une fois appliquées) les réponses 429 ou 5xx sont transitoires : la même requête a de bonnes chances d’aboutir un instant plus tard. Réessayez avec un backoff exponentiel et un peu de jitter. Réessayer est ici sans risque précisément parce que l’API est stateless : chaque appel est une fonction pure de ses entrées, sans écriture partielle à annuler.

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

Ne réessayez pas après un 400 : une erreur de validation ne se corrigera pas toute seule, et le corps application/problem+json vous indique quel champ corriger. Consultez Erreurs pour le catalogue. En ligne de commande, cURL intègre ce mécanisme : --retry 3 --retry-delay 2 --retry-all-errors.

Limites de taille et rate limits

Les limites de taille de requête, les rate limits et leurs codes de statut 413, 429 et apparentés font partie du contrat de l’API à titre réservé et ne sont pas encore appliqués. Aucun seuil fixe n’est publié : concevez pour le contrat plutôt que pour un chiffre précis.

Pour rester robuste à mesure que les limites entrent en vigueur, gardez chaque requête à une taille raisonnable, traitez 413 et 429 comme des cas à réessayer avec backoff (ou, pour 413, comme le signal qu’il faut diviser l’entrée) et respectez un en-tête Retry-After lorsqu’il est présent. Consultez Rate limits et utilisation pour savoir comment l’utilisation est mesurée.

Diviser d’abord les fichiers très volumineux

Si un document unique est trop gros ou trop lent à traiter en un seul appel, découpez-le en parties plus petites, traitez chacune indépendamment et, s’il vous faut récupérer un seul fichier, fusionnez les résultats. Cela plafonne la mémoire et le temps qu’exige une requête donnée, et vous permet de traiter les parties en parallèle.

Divisez en parties

Utilisez Diviser un PDF par taille de fichier ou Diviser un PDF par nombre de pages pour produire des blocs gérables. Consultez Diviser un PDF et traiter la sortie pour savoir comment extraire la sortie.

Traitez chaque partie

Exécutez votre action sur chaque partie, en parallèle si vous le souhaitez, puisque les appels sont indépendants.

Fusionnez si nécessaire

Recombinez les parties traitées avec Fusionner des documents PDF.

Voir aussi