PDF Blocks
PreiseSupport
Kostenlos starten
Seite öffnen

Mit großen Dateien arbeiten

Was sich ändert, sobald die PDFs groß werden: Streaming, Zeitlimits, Wiederholungen, die Größengrenzen und wann Sie besser vorher aufteilen.

Große PDFs sind dieselbe Anfrage wie kleine (eine Datei hinein, eine Datei heraus), aber der naive Ansatz, das ganze Dokument in den Speicher zu lesen und auf ein voreingestelltes Zeitlimit zu warten, bricht zusammen, sobald die Dateien wachsen. Verarbeiten Sie Upload und Download als Stream, geben Sie der Anfrage Raum zum Fertigwerden und wiederholen Sie vorübergehende Fehler. Weil die API stateless ist, hinterlässt eine fehlgeschlagene Anfrage nichts, sodass Wiederholungen sicher sind.

Als Stream verarbeiten, nicht puffern

Lesen Sie die Eingabe beim Senden blockweise von der Festplatte und schreiben Sie die Antwort beim Eintreffen blockweise auf die Festplatte. Halten Sie niemals das ganze Dokument im Speicher. Jedes Beispiel unten verarbeitet beide Richtungen als Stream und bringt ein Wasserzeichen auf big.pdf auf, stellvertretend für jede Aktion mit einer einzigen Ausgabe.

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

Großzügige Zeitlimits setzen

Ein großes Dokument braucht länger zum Hochladen und zum Verarbeiten, deshalb schneidet ein voreingestelltes Zeitlimit im Client, oft 30 Sekunden oder weniger, eine gesunde Anfrage ab. Teilen Sie das Budget auf:

  • Ein kurzes Zeitlimit für den Verbindungsaufbau (ein paar Sekunden) scheitert schnell, wenn der Host nicht erreichbar ist.
  • Ein langes Zeitlimit für das Lesen gibt der Verarbeitung Zeit zum Fertigwerden. Die Beispiele oben verwenden zehn Minuten; bemessen Sie es nach dem größten Dokument, das Sie erwarten.

Setzen Sie kein unbegrenztes Zeitlimit; eine hängende Verbindung soll irgendwann doch aufgeben, damit ein neuer Versuch übernehmen kann.

Vorübergehende Fehler wiederholen

Zurückgesetzte Netzwerkverbindungen, Zeitüberschreitungen beim Verbindungsaufbau und (sobald sie durchgesetzt werden) Antworten mit 429 oder 5xx sind vorübergehend: Dieselbe Anfrage kann einen Moment später durchaus gelingen. Wiederholen Sie mit exponentiellem Backoff und ein wenig Jitter. Eine Wiederholung ist hier genau deshalb sicher, weil die API stateless ist: Jeder Aufruf ist eine reine Funktion seiner Eingaben, ohne teilweise Schreibvorgänge, die rückgängig zu machen wären.

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

Wiederholen Sie kein 400: Ein Validierungsfehler behebt sich nicht von selbst, und der Antworttext vom Typ application/problem+json sagt Ihnen, welches Feld zu korrigieren ist. Den Katalog finden Sie unter Fehler. Auf der Kommandozeile bringt cURL das schon mit: --retry 3 --retry-delay 2 --retry-all-errors.

Größengrenzen und Rate-Limits

Grenzen für die Anfragegröße, Rate-Limits und die zugehörigen Statuscodes 413, 429 und verwandte sind ein reservierter Teil des API-Vertrags und werden noch nicht durchgesetzt. Es werden keine festen Schwellenwerte veröffentlicht. Entwerfen Sie für den Vertrag und nicht für eine bestimmte Zahl.

Damit Ihr Code robust bleibt, sobald die Grenzen in Kraft treten, halten Sie einzelne Anfragen in einer vernünftigen Größe, behandeln Sie 413 und 429 als mit Backoff wiederholbar (bei 413 auch als Signal, die Eingabe aufzuteilen) und beachten Sie einen Header Retry-After, wenn er vorhanden ist. Wie die Nutzung gemessen wird, steht unter Rate-Limits und Nutzung.

Sehr große Dateien zuerst aufteilen

Wenn ein einzelnes Dokument zu groß oder zu langsam ist, um es in einem Aufruf zu verarbeiten, zerlegen Sie es in kleinere Teile, verarbeiten Sie jeden für sich und führen Sie die Ergebnisse zusammen, falls Sie am Ende eine einzige Datei brauchen. Das begrenzt den Speicher und die Zeit, die eine einzelne Anfrage braucht, und Sie können die Teile parallel verarbeiten.

In Teile aufteilen

Verwenden Sie Nach Dateigröße aufteilen oder Nach Seitenanzahl aufteilen, um handhabbare Blöcke zu erzeugen. Wie Sie die Ausgabe auspacken, steht unter Ein PDF aufteilen.

Jeden Teil verarbeiten

Führen Sie Ihre Aktion auf jedem Teil aus, bei Bedarf auch parallel, denn die Aufrufe sind unabhängig.

Bei Bedarf zusammenführen

Setzen Sie die verarbeiteten Teile mit PDF-Dokumente zusammenführen wieder zusammen.

Siehe auch