PDF Blocks
PrezziSupporto
Iniziare gratis
Aprire la pagina

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.

Unire i file di origine

POST /v1/merge_documents con cover.pdf e report.pdf restituisce un unico PDF combinato. Vedere Unire documenti PDF per le opzioni di input.

Apporre la filigrana sui byte uniti

Inviare quel PDF nel campo file a POST /v1/add_text_watermark. La risposta è lo stesso documento con una filigrana su ogni pagina.

Crittografare i byte filigranati

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.

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);

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