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.
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.
Senden Sie dieses PDF im Feld file an POST /v1/add_text_watermark. Die
Antwort ist dasselbe Dokument mit einem Wasserzeichen auf jeder Seite.
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.
# 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# 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.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);<?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);# 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)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)
}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
Dateien als erste Stufe einer Pipeline kombinieren.
Text über die Seiten eines beliebigen PDFs aufbringen.
Die Aktionen für Passwörter und Einschränkungen richtig anordnen.
Eingaben und Ausgaben einer Pipeline als Stream verarbeiten, ohne zu puffern.