在 Netlify Functions 跑 ffmpeg:體積太大打包不進去,改由我們代管執行。
Netlify Functions 跑在 AWS Lambda 上,ffmpeg 一放進去就撞破打包體積上限、超過預設逾時。把這件事交給 Fotovid:送出一個請求,拿回一個現成的結果 URL,任何逾時上限都綽綽有餘。
// netlify/functions/watermark.mts
export default async (req: Request) => {
const { videoUrl, logoUrl } = await req.json();
const res = await fetch("https://api.fotovid.co/v1/video/watermark", {
method: "POST",
headers: {
Authorization: `Bearer ${Netlify.env.get("FOTOVID_API_KEY")}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
source_url: videoUrl,
params: { type: "image", watermark_image_url: logoUrl, position: "bottom-right" },
}),
});
const { url } = await res.json();
return Response.json({ url });
};為什麼 ffmpeg 在 Netlify 上老是失敗
Netlify Functions 在 AWS Lambda 上執行,而 ffmpeg 正是在這裡處處碰壁。靜態編譯的 ffmpeg 二進位檔遠遠超過約 50MB 壓縮 / 250MB 解壓縮的打包上限,結果不是部署直接失敗,就是二進位檔悄悄沒被帶上去。就算你硬塞了進去,預設 10s 的函式逾時也會中斷任何真正的轉檔——改用背景函式只多給你 26s,對大多數影片處理來說還是不夠。這就是典型的「在我筆電上跑得好好的,上線就逾時」陷阱。
- ffmpeg 二進位檔加上相依函式庫,超過 Lambda 解壓縮後 250MB 的打包上限
- 預設 10s 逾時(背景函式 26s)會讓真正的轉檔跑到一半被砍掉
- 冷啟動與 Lambda layer 的東拼西湊,帶來額外延遲和打包上的麻煩
- 本機跑得起來,部署後的函式卻重現不出同樣結果
團隊試過哪些在 Netlify 上跑 ffmpeg 的方法
在 Fotovid 出現之前,你只有這些選擇——而每一條路都是你不會想自己扛的工程。
| Option | What it costs you | Verdict |
|---|---|---|
| 把 ffmpeg 打包進你的 Netlify Function | 超過 250MB 打包上限,還會撞上 10s 的函式逾時 | |
| 自己架容器或虛擬機 | 全天候開機的成本,外加修補、擴充與監控 | |
| 自建任務佇列 + worker | 多一整套基礎設施:佇列、worker、重試、死信處理 | |
| 在瀏覽器裡轉檔(WASM) | 又慢又吃記憶體,在手機上還會直接當掉 | |
| 呼叫 Fotovid API | 一個 HTTPS 請求;沒有東西要跑、要打包,也不用擴充 |
三個步驟完成第一次呼叫
- 1取得免費 API 金鑰
註冊後建立一把金鑰。你用 Authorization: Bearer p6_<key_id>:<secret> 標頭做驗證——不需要二進位檔、不需要 Lambda layer,也沒有任何東西要打包進你的 Netlify Function。
- 2把影片 POST 到浮水印端點
在你的 Netlify Function 裡,對 https://api.fotovid.co 呼叫 POST /v1/video/watermark。送出標準格式:一個指向來源影片的 source_url,加上一個 params 物件——把 type 設成 image、text 或 combo,再挑一個 position(例如 bottom-right),並可搭配 opacity、scale 與 padding。ffmpeg 的活由 Fotovid 在平台外完成,所以你的函式只要發出一個快速的 HTTPS 請求,而不必去呼叫一個它根本帶不上去的二進位檔。
- 3使用代管的結果 URL
回應是扁平的 JSON,含有 id、type、url 和 expires_at。url 指向處理完成、由 Fotovid 代管的影片。你可以直接回傳給前端,或自己留一份——結果只是暫時代管,記得下載後重新存到你平常放素材的地方。
