跳轉到

R2 封存

R2Archival 子系統透過 Cloudflare R2 相容的 S3 API 將 PDF 封存至物件儲存,使用 AWS Signature Version 4(SigV4)認證,並提供預簽名 URL 生成能力,讓使用者在不暴露憑證的情況下取得時效性下載連結。


PHP Compatibility

This example uses PHP 8.5 syntax. If your environment runs PHP 8.1 or 7.4, use NextPDF Backport for a backward-compatible build.

R2 設定

<?php

declare(strict_types=1);

use NextPDF\Cloudflare\R2\R2Client;
use NextPDF\Cloudflare\R2\Config\R2Config;

$r2 = R2Client::create(
    config: R2Config::create(
        accountId:       $_ENV['CF_ACCOUNT_ID'],
        accessKeyId:     $_ENV['R2_ACCESS_KEY_ID'],
        secretAccessKey: $_ENV['R2_SECRET_ACCESS_KEY'],
        bucketName:      'nextpdf-archive',
        region:          'auto',   // R2 使用 'auto' region
        endpoint:        "https://{$_ENV['CF_ACCOUNT_ID']}.r2.cloudflarestorage.com",
    ),
);

安全須知accessKeyIdsecretAccessKey 應從環境變數或 Cloudflare Workers Secrets 讀取,絕不可硬編碼至原始碼。

封存 PDF

use NextPDF\Cloudflare\R2\R2Client;
use NextPDF\Cloudflare\R2\StorageKey;

// 生成 PDF
$pdfBytes = $renderer->render(function (\NextPDF\Core\Document $doc): void {
    $doc->addPage()->text('Archived Document', x: 20, y: 30);
});

// 封存至 R2
$key = StorageKey::generate(
    prefix: 'invoices/',
    identifier: "invoice-{$invoiceId}",
    extension: 'pdf',
    timestamp: true, // 加入時間戳避免覆蓋:invoices/2026/03/04/invoice-42.pdf
);

$result = $r2->put(
    key: $key->toString(),
    body: $pdfBytes,
    metadata: [
        'invoice-id'   => (string) $invoiceId,
        'generated-at' => date('c'),
        'content-type' => 'application/pdf',
    ],
);

AWS Signature V4 認證

R2Client 內部使用 AWS SigV4 演算法簽署每個 API 請求,確保請求的真實性與防重放攻擊:

Request = HTTP Method + URI + Headers + Body
Canonical String = 標準化請求格式
String to Sign   = "AWS4-HMAC-SHA256" + Date + Scope + Hash(Canonical String)
Signature        = HMAC-SHA256(SigningKey, StringToSign)
Authorization    = "AWS4-HMAC-SHA256 Credential=... Signature=..."

R2Client 自動處理以下 SigV4 步驟:

  1. 建立 Canonical Request(含 x-amz-content-sha256 標頭)
  2. 建立 String to Sign(使用 us-east-1 scope,R2 相容 S3 v4)
  3. 衍生簽名金鑰(HMAC 鏈:Date → Region → Service → aws4_request
  4. 計算最終簽名並注入 Authorization 標頭

預簽名 URL

預簽名 URL 允許持有 URL 的任何使用者在指定時間內下載 PDF,無需暴露 R2 憑證:

use NextPDF\Cloudflare\R2\PresignedUrlGenerator;

$generator = PresignedUrlGenerator::create(r2Client: $r2);

$presignedUrl = $generator->getObject(
    key: 'invoices/2026/03/04/invoice-42.pdf',
    expiresInSeconds: 3600,    // URL 有效期 1 小時
    responseContentDisposition: 'attachment; filename="invoice-42.pdf"',
    responseContentType: 'application/pdf',
);

// 回傳給使用者(格式:https://...r2.cloudflarestorage.com/...?X-Amz-Signature=...)
echo $presignedUrl->toString();
echo $presignedUrl->expiresAt()->toIso8601String();

列出物件

// 列出特定前綴下的所有物件
$objects = $r2->listObjects(
    prefix: 'invoices/2026/03/',
    maxKeys: 1000,
);

foreach ($objects as $object) {
    echo $object->key();          // non-empty-string
    echo $object->size();         // positive-int (bytes)
    echo $object->lastModified(); // DateTimeImmutable
    echo $object->etag();         // non-empty-string
}

刪除與生命週期

// 刪除單一物件
$r2->delete(key: 'invoices/2026/03/04/invoice-42.pdf');

// 批次刪除(最多 1000 個)
$r2->deleteMultiple(keys: [
    'invoices/2026/01/report.pdf',
    'invoices/2026/02/report.pdf',
]);

透過 Cloudflare Dashboard 或 API 設定 R2 生命週期規則,自動刪除超過保留期限的物件(推薦取代手動刪除)。

例外處理

例外類別 觸發條件
R2AuthException SigV4 簽名驗證失敗、憑證錯誤
R2BucketNotFoundException Bucket 不存在或無存取權限
R2KeyNotFoundException 嘗試存取不存在的物件
R2NetworkException HTTP 請求失敗(PSR-18 層級)
R2ThrottleException 請求速率超過 R2 API 上限

參見