PDF Blocks
PreiseSupport
Kostenlos starten
Seite öffnen

Aktionen verketten

Wie Sie die Ausgabe einer Aktion direkt an die nächste übergeben, mit einer ausgearbeiteten dreistufigen Pipeline und den Fällen, in denen Verketten das falsche Werkzeug ist.

Jede Aktion nimmt ein PDF als Eingabe entgegen und gibt ein PDF als Ausgabe zurück, sodass die Ausgabe einer Aktion eine gültige Eingabe für die nächste ist. Damit bauen Sie aus Aktionen mit einem einzigen Zweck eine mehrstufige Pipeline: ein paar Dateien zusammenführen, auf dem Ergebnis ein Wasserzeichen aufbringen, es dann verschlüsseln, alles in einem einzigen Skript, wobei die Bytes von Aufruf zu Aufruf im Speicher bleiben.

Weil die API stateless ist (zwischen Anfragen wird nichts gespeichert), gibt es kein Handle zum Wiederverwenden und nichts aufzuräumen. Jede Stufe übergibt einfach ihren Antworttext im Feld file an die nächste Stufe.

So funktioniert es

Eine Aktion mit einer einzigen Ausgabe antwortet mit 200 OK und Content-Type: application/pdf. Der Antworttext ist ein vollständiges PDF. Zum Verketten nehmen Sie diesen Text und senden ihn als Teil file der nächsten Anfrage, statt eine Datei von der Festplatte zu lesen. Nur die allererste Eingabe und die allerletzte Ausgabe müssen das Dateisystem berühren; alles dazwischen bleibt im Speicher.

Eine dreistufige Pipeline

Dieses Beispiel macht aus zwei Quelldateien ein einziges verschlüsseltes Dokument mit Wasserzeichen.

Die Quelldateien zusammenführen

POST /v1/merge_documents mit cover.pdf und report.pdf gibt ein einziges kombiniertes PDF zurück. Die Eingabeoptionen finden Sie unter Dokumente zusammenführen.

Ein Wasserzeichen auf die zusammengeführten Bytes aufbringen

Senden Sie dieses PDF im Feld file an POST /v1/add_text_watermark. Die Antwort ist dasselbe Dokument mit einem Wasserzeichen auf jeder Seite.

Die Bytes mit Wasserzeichen verschlüsseln

Senden Sie das PDF mit Wasserzeichen an POST /v1/add_password mit einem password. Die Antwort ist das fertige, geschützte Dokument. Schreiben Sie es auf die Festplatte.

Das Zusammenführen nimmt die Eingabe-PDFs als wiederholte Teile file entgegen oder, wo das Wiederholen eines Feldnamens unhandlich ist (PHP, Ruby), als nummerierte Felder file_1 bis file_10. Beides ist unten gezeigt; verwenden Sie das wiederholte file, wenn Sie mehr als zehn haben.

cURLbash
# Each stage reads the previous PDF from stdin via `-F 'file=@-'`.
curl -sS https://api.pdfblocks.com/v1/merge_documents \
  -H 'X-API-Key: your_api_key' \
  -F file=@cover.pdf \
  -F file=@report.pdf |
curl -sS https://api.pdfblocks.com/v1/add_text_watermark \
  -H 'X-API-Key: your_api_key' \
  -F 'file=@-;filename=merged.pdf;type=application/pdf' \
  -F line_1='CONFIDENTIAL' \
  -F line_2='ACME, Inc.' |
curl -sS https://api.pdfblocks.com/v1/add_password \
  -H 'X-API-Key: your_api_key' \
  -F 'file=@-;filename=watermarked.pdf;type=application/pdf' \
  -F password='pa$$word' \
  -o final.pdf
Pythonpython
# pip install requests
import requests

BASE = 'https://api.pdfblocks.com'
HEADERS = {'X-API-Key': 'your_api_key'}

# 1. Merge cover.pdf and report.pdf into one document.
with open('cover.pdf', 'rb') as cover, open('report.pdf', 'rb') as report:
    merged = requests.post(
        f'{BASE}/v1/merge_documents',
        headers=HEADERS,
        files=[('file', cover), ('file', report)],
    )
merged.raise_for_status()

# 2. Watermark the merged bytes: no temp file.
watermarked = requests.post(
    f'{BASE}/v1/add_text_watermark',
    headers=HEADERS,
    files={'file': ('merged.pdf', merged.content, 'application/pdf')},
    data={'line_1': 'CONFIDENTIAL', 'line_2': 'ACME, Inc.'},
)
watermarked.raise_for_status()

# 3. Encrypt the watermarked bytes.
final = requests.post(
    f'{BASE}/v1/add_password',
    headers=HEADERS,
    files={'file': ('watermarked.pdf', watermarked.content, 'application/pdf')},
    data={'password': 'pa$$word'},
)
final.raise_for_status()

with open('final.pdf', 'wb') as output:
    output.write(final.content)
Node.jsjavascript
// Node.js 18+
import { readFile, writeFile } from 'node:fs/promises';

const BASE = 'https://api.pdfblocks.com';
const headers = { 'X-API-Key': 'your_api_key' };

// Post a multipart form and return the response PDF as bytes.
async function post(path, form) {
  const response = await fetch(BASE + path, { method: 'POST', headers, body: form });
  if (!response.ok) throw new Error(`${path} failed: ${response.status}`);
  return new Uint8Array(await response.arrayBuffer());
}

// 1. Merge cover.pdf and report.pdf.
const mergeForm = new FormData();
mergeForm.append('file', new Blob([await readFile('cover.pdf')]), 'cover.pdf');
mergeForm.append('file', new Blob([await readFile('report.pdf')]), 'report.pdf');
const merged = await post('/v1/merge_documents', mergeForm);

// 2. Watermark the merged bytes.
const watermarkForm = new FormData();
watermarkForm.append('file', new Blob([merged]), 'merged.pdf');
watermarkForm.append('line_1', 'CONFIDENTIAL');
watermarkForm.append('line_2', 'ACME, Inc.');
const watermarked = await post('/v1/add_text_watermark', watermarkForm);

// 3. Encrypt the watermarked bytes.
const passwordForm = new FormData();
passwordForm.append('file', new Blob([watermarked]), 'watermarked.pdf');
passwordForm.append('password', 'pa$$word');
const final = await post('/v1/add_password', passwordForm);

await writeFile('final.pdf', final);
PHPphp
<?php
$base = 'https://api.pdfblocks.com';
$headers = ['X-API-Key: your_api_key'];

// Post a multipart form and return the response body, or throw on error.
function post(string $url, array $headers, array $fields): string {
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => $headers,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => $fields,
    ]);
    $body = curl_exec($ch);
    $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    if ($status !== 200) {
        throw new RuntimeException("Request to $url failed with status $status.");
    }
    return $body;
}

// 1. Merge cover.pdf and report.pdf (numbered fields for the array input).
$merged = post("$base/v1/merge_documents", $headers, [
    'file_1' => new CURLFile('cover.pdf', 'application/pdf'),
    'file_2' => new CURLFile('report.pdf', 'application/pdf'),
]);

// 2. Watermark the merged bytes in memory (CURLStringFile, PHP 8.1+).
$watermarked = post("$base/v1/add_text_watermark", $headers, [
    'file' => new CURLStringFile($merged, 'merged.pdf', 'application/pdf'),
    'line_1' => 'CONFIDENTIAL',
    'line_2' => 'ACME, Inc.',
]);

// 3. Encrypt the watermarked bytes.
$final = post("$base/v1/add_password", $headers, [
    'file' => new CURLStringFile($watermarked, 'watermarked.pdf', 'application/pdf'),
    'password' => 'pa$$word',
]);

file_put_contents('final.pdf', $final);
Rubyruby
# gem install http
require 'http'
require 'stringio'

BASE = 'https://api.pdfblocks.com'
HEADERS = { 'X-API-Key' => 'your_api_key' }

def post(path, fields)
  response = HTTP.headers(HEADERS).post("#{BASE}#{path}", form: fields)
  raise "#{path} failed: #{response.status}" unless response.status.success?
  response.body.to_s
end

# 1. Merge cover.pdf and report.pdf (numbered fields for the array input).
merged = post('/v1/merge_documents',
  file_1: HTTP::FormData::File.new('cover.pdf'),
  file_2: HTTP::FormData::File.new('report.pdf'))

# 2. Watermark the merged bytes in memory.
watermarked = post('/v1/add_text_watermark',
  file: HTTP::FormData::File.new(StringIO.new(merged),
    filename: 'merged.pdf', content_type: 'application/pdf'),
  line_1: 'CONFIDENTIAL',
  line_2: 'ACME, Inc.')

# 3. Encrypt the watermarked bytes.
final = post('/v1/add_password',
  file: HTTP::FormData::File.new(StringIO.new(watermarked),
    filename: 'watermarked.pdf', content_type: 'application/pdf'),
  password: 'pa$$word')

File.write('final.pdf', final)
Gogo
package main

import (
	"bytes"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"os"
)

const base = "https://api.pdfblocks.com"

// post sends a multipart form and returns the response PDF bytes.
func post(path string, build func(*multipart.Writer)) []byte {
	var buf bytes.Buffer
	form := multipart.NewWriter(&buf)
	build(form)
	form.Close()

	req, _ := http.NewRequest("POST", base+path, &buf)
	req.Header.Set("Content-Type", form.FormDataContentType())
	req.Header.Set("X-API-Key", "your_api_key")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	body, _ := io.ReadAll(res.Body)
	if res.StatusCode != http.StatusOK {
		panic(fmt.Sprintf("%s failed: %d", path, res.StatusCode))
	}
	return body
}

func addFile(form *multipart.Writer, name string, data []byte) {
	part, _ := form.CreateFormFile("file", name)
	part.Write(data)
}

func main() {
	cover, _ := os.ReadFile("cover.pdf")
	report, _ := os.ReadFile("report.pdf")

	// 1. Merge cover.pdf and report.pdf.
	merged := post("/v1/merge_documents", func(form *multipart.Writer) {
		addFile(form, "cover.pdf", cover)
		addFile(form, "report.pdf", report)
	})

	// 2. Watermark the merged bytes.
	watermarked := post("/v1/add_text_watermark", func(form *multipart.Writer) {
		addFile(form, "merged.pdf", merged)
		form.WriteField("line_1", "CONFIDENTIAL")
		form.WriteField("line_2", "ACME, Inc.")
	})

	// 3. Encrypt the watermarked bytes.
	final := post("/v1/add_password", func(form *multipart.Writer) {
		addFile(form, "watermarked.pdf", watermarked)
		form.WriteField("password", "pa$$word")
	})

	os.WriteFile("final.pdf", final, 0644)
}
C#csharp
using System.Net.Http.Headers;

const string Base = "https://api.pdfblocks.com";

using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "your_api_key");

// Post a multipart form and return the response PDF bytes.
async Task<byte[]> Post(string path, Action<MultipartFormDataContent> build)
{
    using var form = new MultipartFormDataContent();
    build(form);
    var response = await client.PostAsync(Base + path, form);
    response.EnsureSuccessStatusCode();
    return await response.Content.ReadAsByteArrayAsync();
}

static void AddFile(MultipartFormDataContent form, string name, byte[] data)
{
    var content = new ByteArrayContent(data);
    content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
    form.Add(content, "file", name);
}

// 1. Merge cover.pdf and report.pdf.
var merged = await Post("/v1/merge_documents", form =>
{
    AddFile(form, "cover.pdf", File.ReadAllBytes("cover.pdf"));
    AddFile(form, "report.pdf", File.ReadAllBytes("report.pdf"));
});

// 2. Watermark the merged bytes.
var watermarked = await Post("/v1/add_text_watermark", form =>
{
    AddFile(form, "merged.pdf", merged);
    form.Add(new StringContent("CONFIDENTIAL"), "line_1");
    form.Add(new StringContent("ACME, Inc."), "line_2");
});

// 3. Encrypt the watermarked bytes.
var final = await Post("/v1/add_password", form =>
{
    AddFile(form, "watermarked.pdf", watermarked);
    form.Add(new StringContent("pa$$word"), "password");
});

await File.WriteAllBytesAsync("final.pdf", final);

Jede Stufe prüfen, bevor sie weitergereicht wird

Eine fehlgeschlagene Stufe gibt einen 4XX mit einem Antworttext vom Typ application/problem+json zurück, kein PDF. Wenn Sie diesen Fehlertext als file in die nächste Anfrage leiten, schlägt auch die nächste Stufe fehl, mit einer verwirrenden Meldung. Prüfen Sie den Status jeder Antwort, bevor Sie ihren Text weiterreichen: Die Beispiele oben lösen bei allem außer 200 einen Fehler aus. Die Form der Antwort finden Sie unter Fehler.

Bringen Sie Wasserzeichen auf und führen Sie zusammen, bevor Sie verschlüsseln. Sobald Passwort hinzufügen ein Dokument verschlüsselt hat, können spätere Aktionen es nicht mehr öffnen; jeder Schritt mit einem Passwort gehört daher ans Ende der Pipeline. Alle Regeln zur Reihenfolge finden Sie unter Dokumente schützen und entsperren.

Wann Sie verketten sollten und wann nicht

Verketten Sie, wenn jeder Schritt eine eigenständige Transformation ist, die Sie der Reihe nach anwenden wollen. Jeder Schritt sollte ein reiner Aufruf mit einem einzigen Zweck bleiben: Genau das macht die Ausgabe des einen zu einer gültigen Eingabe für den nächsten, und genau das sorgt dafür, dass sich Fehler leicht eingrenzen lassen.

Manche Aufgaben sehen nach Verketten aus, sind es aber nicht. Seiten in einem Durchgang neu anzuordnen und dabei welche zu verwerfen, ist ein einziger Aufruf von Seiten neu anordnen, kein Extrahieren mit anschließendem Zusammenführen. Greifen Sie erst dann zu einer Pipeline, wenn keine einzelne Aktion die ganze Arbeit erledigt.

Siehe auch