Bannerify
所有文章

用 API 在 PHP 中生成動態圖片

只要設計一次範本,就能從 PHP 生成橫幅、商品圖與證書。可用純 cURL 或 bannerify/bannerify SDK,不需要 GD 或 Imagick。

· Duc An

多數 PHP 的圖片程式碼,其實都是排版程式碼。用 GD 或 Imagick 時,每個設計決定都會變成一次函式呼叫:

$image = imagecreatetruecolor(1200, 630);
$blue = imagecolorallocate($image, 37, 99, 235);
imagefilledrectangle($image, 0, 0, 1200, 630, $blue);
imagettftext($image, 48, 0, 80, 300, $white, 'Inter-Bold.ttf', 'Summer sale: 30% off');

這樣做可以運作,直到有人想把標題拆成兩行、在價格後面加陰影,或在角落放一個 QR code。這時設計就卡在你的 PHP 裡,每次微調都要改程式碼、再部署一次。

在「自己畫」和「架一整群無頭瀏覽器」之間,還有第三種做法:把版面留在範本裡,PHP 只負責把資料傳給圖片生成 API。

API 版本的運作方式

  1. 在編輯器裡設計一次橫幅,或從範本庫複製一個範本。
  2. 為之後要修改的圖層命名,例如 headline、price、photo。
  3. 把範本 ID 和你的資料送到 API,回應就是完成的圖片。

字型、間距、漸層、陰影、表格和 QR code 都留在範本裡。設計師可以修改這一切,完全不必動到你的 PHP;而你的 PHP 繼續做它擅長的事:搬運資料。

你需要準備什麼

  • API 金鑰。 在 儀表板 → API 金鑰 建立,並且只放在伺服器上。把它當成資料庫密碼來保管。
  • 範本 ID。 開啟你設計好的範本,複製它的 ID,格式像 tpl_kd93mz。
  • 圖層名稱。 你在編輯器中為圖層取的名稱,就是請求裡要用的名稱,必須完全一致。

做法一:純 cURL

不需要任何相依套件,在任何 PHP 主機上都能用,包括那些連安裝擴充套件都很麻煩的主機。

<?php

$payload = [
    'apiKey' => getenv('BANNERIFY_API_KEY'),
    'templateId' => 'tpl_kd93mz',
    'format' => 'png',
    'modifications' => [
        ['name' => 'headline', 'text' => 'Summer sale: 30% off'],
        ['name' => 'price', 'text' => '$149.00'],
        ['name' => 'photo', 'src' => 'https://example.com/sneaker.jpg'],
    ],
];

$ch = curl_init('https://api.bannerify.co/v1/templates/createImage');

curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
    CURLOPT_POSTFIELDS => json_encode($payload),
    CURLOPT_TIMEOUT => 30,
]);

$image = curl_exec($ch);

if ($image === false) {
    throw new RuntimeException('Transport error: ' . curl_error($ch));
}

$status = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($status !== 200) {
    throw new RuntimeException("Bannerify returned {$status}: " . substr($image, 0, 200));
}

file_put_contents(__DIR__ . '/banner.png', $image);

有兩個細節常讓人踩雷:

  • 回應內容本身就是圖片,不是內含 base64 的 JSON。直接寫入檔案,或串流輸出到瀏覽器即可。
  • 錯誤會以 JSON 搭配 4xx 狀態碼回傳。 所以上面的程式碼會先檢查狀態碼,才儲存檔案。

做法二:官方 PHP SDK

如果你不想自己寫請求,可以安裝 SDK:

composer require bannerify/bannerify

SDK 只會回傳 result 或 error 其中之一,不會同時出現,所以不可能忘記處理失敗的情況:

<?php

use Bannerify\Bannerify\BannerifyClient;

$client = new BannerifyClient(getenv('BANNERIFY_API_KEY'));

$result = $client->createImage('tpl_kd93mz', [
    'modifications' => [
        ['name' => 'headline', 'text' => $product['name']],
        ['name' => 'price', 'text' => '$' . number_format($product['price'], 2)],
        ['name' => 'photo', 'src' => $product['image_url']],
    ],
]);

if (isset($result['error'])) {
    error_log('Render failed: ' . $result['error']['message']);

    return null;
}

$path = __DIR__ . "/banners/{$product['sku']}.png";
file_put_contents($path, $result['result']);

完整的方法清單與選項請見 PHP SDK 文件。

送入真實資料

你不需要手動組出 modifications 陣列。API 也支援物件簡寫,以圖層名稱當作 key:

'modifications' => [
    'headline' => $product['name'],
    'price' => '$' . number_format($product['price'], 2),
    'photo' => ['src' => $product['image_url']],
    'qr' => ['qrcode' => $product['checkout_url']],
],

一般的值會被當成文字。巢狀陣列則保留各自的欄位,圖片、QR code、條碼、圖表和表格都需要這種寫法。

每個圖層可以送出的欄位:

  • text 取代文字圖層的內容
  • color 覆寫圖層顏色,例如 #FF0000
  • src 替換圖片圖層,可使用任何公開可存取的 HTTPS 網址
  • qrcode 和 barcode 用來編碼連結、SKU 或追蹤編號
  • rows 和 columns 用來填入圖表與表格
  • visible 只在單次生成中隱藏圖層
  • star 設定評分數值

每個圖層一個欄位,依名稱送出。如果兩個圖層需要相同的資料,就送兩次。

批次處理與快取

生成一千張圖片就是跑一個迴圈,不是什麼特殊情況:

foreach ($products as $product) {
    $payload = buildPayload($product);
    $cacheKey = hash('xxh128', json_encode($payload));
    $path = __DIR__ . "/cache/{$cacheKey}.png";

    if (file_exists($path)) {
        continue;
    }

    file_put_contents($path, renderBanner($payload));
}

迴圈要設上限(同時只送出幾個請求),並以 payload 作為快取的 key,這樣重複的請求就不會重複付費。生成是同步的,所以你在同一個請求裡就能拿到檔案,不必輪詢任務佇列。

如果你完全不想儲存檔案,可以改用 createStoredImage。它會上傳生成的圖片,並回傳一個託管的網址:

{ "url": "https://cdn.bannerify.co/..." }

對於 Open Graph 圖片、電子郵件標頭,以及任何用網址比用檔案更方便傳遞的場合,這是最方便的做法。

格式與尺寸

  • format 支援 png(預設)、jpeg 或 webp。
  • 需要 PDF?同一個範本可以用 createPdf 輸出成 PDF。細節請見 PDF 生成 API 頁面,PHP 程式碼請見發票 PDF 指南。
  • 畫布尺寸由範本決定,不是由請求決定。每種需要的尺寸各留一個範本:連結預覽用 1200x630,動態消息用 1080x1080,限時動態用 1080x1920。
  • 要用 src 傳照片嗎?渲染器會去抓取那個網址,所以網址必須是公開的。本機檔案或私有的儲存空間都會失敗。

常見錯誤

  • 把 API 金鑰放在瀏覽器的 JavaScript 或行動 App 裡。它應該放在伺服器上。
  • 內容沒變,卻重新生成圖片。請以 payload 的雜湊值做快取。
  • 在單一個網頁請求裡生成上千張圖片。幾十張還行,上千張請用佇列。
  • 以為回應是 JSON。除非使用 stored 端點,否則回應就是檔案本身。

費用

免費方案每月包含 100 次生成(每天 10 次)、一個範本和 1GB 的圖片儲存空間,足夠讓你先把它接進真實的應用程式。Automate 方案每月 $29 起,包含 3,000 次生成與不限數量的範本。詳情請見價格。

如果你現在是用 PHP 生成圖片,可以從免費方案開始,先把一個範本從 GD 搬走。第一張版面完整無誤的圖片回傳時,通常就是大家不再寫座標定位程式碼的時刻。

前 100 張圖片,免費送你。

做一個範本、取得 API 金鑰,幾分鐘內就能生成第一張圖片。