在 Day 15 文章中,我們處理了 ImageMap Message 的背景圖,接著我們遇到了圖片不能出現副檔名及動態路徑支援不同解析度的問題。今天主要會透過 Cloudflare Workers 來解決這兩個問題,讓 Cloudflare Workers 達到圖片代理的作用。
ImageMap 處理流程涉及多個工具協作,整體步驟如下:
今天主要會說明 4 ~ 5 項。
本日程式碼的範例連結
CloudFlare Worker 是一個 Serverless 服務平台,以 Function 為部署單位,適合處理這類輕量級的 URL 轉換需求。
我們可以透過 Cloudflare Workers 來處理請求轉向。當系統接收到包含 public_id
、width
及 imageType
路由參數組合的請求時,Worker 會自動將請求重新導向至對應解析度的 Cloudinary 圖片 URL。
Step 1:登入 CloudFlare 官網
登入後點選左側選單的「Workers」選項,接著點擊右側的「建立」按鈕開始建立新的 Worker。
Step 2:選擇建立方式
這邊選擇使用 Hello World 建立方式
Step 3:設定 Worker 名稱
為 Worker 取一個有意義的名稱,這裡命名為
linebot-imagemap
,需要注意此命名會直接影響最終 Worker 服務的網址
Step 4:完成部署並編輯代碼
系統顯示已成功部署 Worker,點選右上角的「編輯代碼」選項即可開始撰寫程式碼。
Step 5:驗證 Worker 服務運行狀態
當頁面顯示「Hello World!」時,表示 Worker 服務已正常運行並能夠響應請求。
這邊注意 Worker 服務的網址格式為
https://{Worker名稱}.{帳戶名稱}.workers.dev/
Step 6:修改 Function 內容以符合圖片導向規則
Worker 需要支援
baseUrl/{image width}
的請求格式
目標是透過路由參數取得 Cloudinary 圖片讀取所需的參數:
將 baseUrl
定義為 {Worker 服務網址}/{publicId}/{imageType}
的格式,讓 LINE 平台能夠將寬度參數附加在路由路徑上。
應用範例:
假設圖片 publicId
為 ImageMap_02_escz9a
,副檔名為 png
:
https://{Worker名稱}.{你的帳戶}.workers.dev/ImageMap_02_escz9a/png
https://{Worker名稱}.{你的帳戶}.workers.dev/ImageMap_02_escz9a/png/240
Worker Function 程式碼修改
export default {
async fetch(request) {
const url = new URL(request.url); // 解析請求 URL
const pathSegments = url.pathname.split('/').filter(segment => segment !== '');
// 解析請求網址取得路由參數 publicId、imageType 跟 width
const [publicId, imageType, width] = pathSegments
const cloudinaryUrl = `https://res.cloudinary.com/dseg0uwc9/image/upload/w_${width},f_auto,q_auto/v1754466769/2025%20IT%20%E9%90%B5%E4%BA%BA%E8%B3%BD/${publicId}.${imageType}`;
return fetch(cloudinaryUrl);
}
};
Step 7:重新佈署 worker
修改後要記得重新部署,看到部署成功才代表已成功更新 Worker Function 程式碼
Step 8:試著把路由參數補上,看能不能成功顯示 Cloudinary 圖片
在右側導覽列的當前 worker 網址後方補上對應的路由參數 publicId
、imageType
及 width
,即可看到對應解析度的圖片!
這樣一來,未來如果要替換其他 ImageMap 背景圖時,只需要修改 publicId
和 imageType
參數,就能快速製作出 LINE ImageMap Message 所需的 baseUrl
。
進階:加入參數驗證機制
為了避免非法請求造成問題,建議為路由參數加上驗證機制。由於 ImageMap 僅支援
jpeg
及png
格式,因此需要限制可接受的圖片類型。
Worker Function 完整程式碼
// 驗證函式
function validateParameters(publicId, imageType, width) {
// 驗證參數完整性
if (!publicId || !imageType || !width) {
return 'Missing required parameters. Expected format: /{publicId}/{imageType}/{width}';
}
// 驗證 width 是否為有效數字
const widthNum = parseInt(width);
if (isNaN(widthNum) || widthNum <= 0) {
return 'Width must be a positive number';
}
// 驗證圖片類型
const validImageTypes = ['jpeg', 'png'];
if (!validImageTypes.includes(imageType.toLowerCase())) {
return `Invalid image type. Supported types: ${validImageTypes.join(', ')}`;
}
// 驗證 width 是否為 Line ImageMap 支援的尺寸
const validWidths = [240, 300, 460, 700, 1040];
if (!validWidths.includes(widthNum)) {
return `Invalid width. Supported widths: ${validWidths.join(', ')}`;
}
return null; // 驗證通過
}
export default {
async fetch(request) {
const url = new URL(request.url); // 解析請求 URL
const pathSegments = url.pathname.split('/').filter(segment => segment !== '');
const [publicId, imageType, width] = pathSegments // 取得路由參數 publicId、imageType 跟 width
const validationError = validateParameters(publicId, imageType, width);
if (validationError) {
return new Response(validationError, {
status: 400,
headers: { 'Content-Type': 'text/plain' }
});
}
const cloudinaryUrl = `https://res.cloudinary.com/dseg0uwc9/image/upload/w_${width},f_auto,q_auto/v1754466769/2025%20IT%20%E9%90%B5%E4%BA%BA%E8%B3%BD/${publicId}.${imageType}`;
return fetch(cloudinaryUrl);
}
};
至此,ImageMap
圖片的 baseUrl
部分已經準備就緒,接下來將功能整合到 line-message
模組中。
ImageMap Message 在處理上相對其他訊息類型更為複雜,特別是在圖片 URL 的規格限制上。透過 CloudFlare Worker 搭配 Cloudinary 的組合,就可以輕鬆處理好 ImageMap 的圖片問題。
由於 ImageMap 和 FlexMessage 都屬於參數較多、設定較為複雜的訊息類型,建議在開發時搭配 GUI 工具來簡化設置流程,降低手動配置的負擔。