Concatenare le azioni
Come passare l’output di un’azione direttamente alla successiva, con un esempio completo di pipeline in tre fasi e i casi in cui concatenare non è lo strumento giusto.
Ogni azione riceve un PDF e ne restituisce uno, quindi l’output di un’azione è un input valido per la successiva. Questo permette di costruire una pipeline in più passaggi a partire da azioni a scopo unico: unire qualche file, apporre una filigrana sul risultato e poi crittografarlo, tutto in un solo script, passando i byte da una chiamata all’altra in memoria.
Poiché l’API è stateless (tra una richiesta e l’altra non viene memorizzato
nulla), non c’è alcun riferimento da riutilizzare né alcuna pulizia da fare.
Ogni fase si limita a passare il corpo della propria risposta alla fase
successiva nel campo file.
Come funziona
Un’azione a output singolo risponde con 200 OK e Content-Type: application/pdf. Il corpo è un PDF completo. Per concatenare, si prende quel
corpo e lo si invia come parte file della richiesta successiva, invece di
leggere un file dal disco. Solo il primissimo input e l’ultimissimo output
devono toccare il filesystem; tutto ciò che sta in mezzo resta in memoria.
Una pipeline in tre fasi
Questo esempio trasforma due file di origine in un unico documento crittografato e filigranato.
POST /v1/merge_documents con cover.pdf e report.pdf restituisce un unico
PDF combinato. Vedere Unire documenti PDF per
le opzioni di input.
Inviare quel PDF nel campo file a POST /v1/add_text_watermark. La risposta
è lo stesso documento con una filigrana su ogni pagina.
Inviare il PDF filigranato a POST /v1/add_password con una password. La
risposta è il documento finale, protetto. Scriverlo su disco.
L’unione accetta i PDF di input come parti file ripetute oppure, dove
ripetere il nome di un campo è scomodo (PHP, Ruby), come campi numerati da
file_1 a file_10. Qui sotto sono mostrate entrambe le forme; usare file
ripetuto quando i documenti sono più di dieci.
# 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);Controllare ogni fase prima di passarne l’output alla successiva
Una fase non riuscita restituisce un 4XX con un corpo
application/problem+json, non un PDF. Se quel corpo di errore viene immesso
nella richiesta successiva come file, anche la fase successiva fallisce, con
un messaggio poco chiaro. Controllare lo stato di ogni risposta prima di
passarne il corpo: gli esempi qui sopra sollevano un errore su qualsiasi
risposta diversa da 200. Vedere Errori per la forma della
risposta.
Apporre la filigrana e unire prima di crittografare. Una volta che Aggiungere una password ha crittografato un documento, le azioni successive non possono più aprirlo, quindi qualsiasi passaggio con password va alla fine della pipeline. Vedere Proteggere e sbloccare i documenti per tutte le regole sull’ordine.
Quando concatenare e quando no
Concatenare quando ogni passaggio è una trasformazione distinta da applicare in sequenza. Mantenere ogni passaggio una chiamata pura, a scopo unico: è questo che rende l’output di uno un input valido per il successivo e che rende facile individuare i guasti.
Alcuni lavori sembrano una concatenazione ma non lo sono. Riordinare e scartare le pagine in un solo passaggio è un’unica chiamata a Riordinare le pagine, non un’estrazione seguita da un’unione. Ricorrere a una pipeline solo quando nessuna singola azione fa tutto il lavoro.
Vedi anche
Unire i file come prima fase di una pipeline.
Apporre del testo sulle pagine di qualsiasi PDF.
Mettere nell’ordine giusto le azioni di password e restrizione.
Trasmettere in streaming input e output di una pipeline senza bufferizzarli.