透過 EventBridge 做定時操作前面我們玩過了,像是幫 EC2 上下班打卡的那種。今天要來點新花樣:不是定時,而是「有事才找人」的那種觸發事件。
說白一點,就是 S3 幫你當主管,Event 當小秘書,Lambda 當實習生。流程是這樣:
ready/
資料夾。這樣一來,網站使用者就不用再被 2.5MB 的大圖炸流量,變成 80KB 的小清新,瀏覽體驗直線上升。工程師也不用再打開 Photoshop 一張一張手工壓圖,省下來的時間剛好可以拿去滑手機、喝咖啡,或者研究怎麼讓 Lambda 幫自己洗衣服(可惜做不到)。
整條 AWS 縮圖流水線就像工廠:
架構圖如下(沒圖不專業,但就算有圖你也還是得自己設定):
接下來,我們就要正式把這條「AWS 自動壓圖生產線」組起來啦
一切從 S3 Bucket 開始,畢竟在 AWS 的世界裡,沒有桶子你就啥都放不了。所以第一步:開一個 S3 bucket。
我們取一個 webimage + 亂碼風格名字:
webimage-430299fd-d75a-46e5
(看起來就像是喝醉後亂按鍵盤的作品,但至少保證不會撞名 😏)
接著,我們在這個 bucket 裡分兩個小房間:
working/
:肥圖專區,所有剛上傳、還沒減肥的高解析度圖檔都先丟這裡。ready/
:修圖完畢 VIP 區,所有被 Lambda 壓到 1024×768 的瘦身成功作品會乖乖放這邊。其他設定?別想太多,全都用預設值就好。工程師的座右銘就是:能不改設定就不改,因為多改一個選項,多一個坑等你跳。
好了,時間到了,把「肥滋滋的大圖」交給我們的無名英雄 —— Lambda。流程很簡單也很簡單:使用者把 jpg
丟到 webimage-430299fd-d75a-46e5/working/
,Lambda 抓去做身材管理,最後把瘦身成功的成品丟到 webimage-430299fd-d75a-46e5/ready/
。
簡單一句話:上傳 → Lambda 出馬 → 壓成 1024×768 → 放到 ready。工程師只要坐等成果,不用再一張一張手動按存檔。
小提醒:我們的範例只專攻
jpg
(也就是 JPG 的專屬健身房)。要是你想讓 PNG、WEBP、GIF 也來報到,程式碼跟相依套件可能要一起升級、改寫、做些小動作 —— 就像不同體型的人做不同熱身一樣。
下面請用這些參數建立一個 Lambda:
processImage
import boto3
import os
import tempfile
from PIL import Image
s3_client = boto3.client("s3")
def lambda_handler(event, context):
# 從 EventBridge/S3 event 拿到檔案資訊
record = event["Records"][0]
bucket_name = record["s3"]["bucket"]["name"]
object_key = record["s3"]["object"]["key"]
# 只處理 working/ 底下的檔案
if not object_key.startswith("working/"):
print(f"Skip file: {object_key}")
return {"status": "skipped"}
print(f"Processing file: s3://{bucket_name}/{object_key}")
# 建立暫存檔路徑
download_path = os.path.join(tempfile.gettempdir(), os.path.basename(object_key))
upload_key = object_key.replace("working/", "ready/", 1)
try:
# 下載檔案
s3_client.download_file(bucket_name, object_key, download_path)
# 開啟圖片並縮放
with Image.open(download_path) as img:
img = img.convert("RGB") # 保險一點,避免奇怪的格式
img = img.resize((1024, 768), Image.LANCZOS)
# 儲存壓縮後檔案到暫存路徑
resized_path = download_path + "_resized.jpg"
img.save(resized_path, "JPEG")
# 上傳回 S3
s3_client.upload_file(resized_path, bucket_name, upload_key)
print(f"Uploaded resized image to s3://{bucket_name}/{upload_key}")
return {"status": "success", "resized_key": upload_key}
except Exception as e:
print(f"Error processing file {object_key}: {e}")
raise
別忘了,我們的程式裡頭有一段:from PIL import Image
這意思是什麼?就是 Lambda 原廠沒附這個零件,結果你叫它處理圖片,它只會傻眼回你:「哥,我光靠原廠附贈的功能,只能幫你印 Hello World
,修圖這種高級服務請自己帶工具來。」
解法很簡單 — 把 Pillow
打包成一個 Layer
,再幫 Lambda 加掛,就像替打工仔配一副 Photoshop 套件,不然它連圖片都打不開。
打開 Lambda 的設定頁面,你會看到「Layers」那一小塊。點下去,然後… 一直往下滑,才會看到它躲在最下面。
接著按右上角的 Add a layer,進入新畫面。
這裡選 Custom Layers → 下拉選單挑 PY313-pillow
(這就是我們上篇 DIY 包好的 Pillow 套件)。記得挑最新版本,舊版本就像過期牛奶,誰敢喝?然後點 Add。
搞定後,你會看到 Pillow 已經掛到 Lambda 身上了,這時候它終於有能力幫你處理圖片了。
最後提醒一個大坑:把 Lambda 的 Timeout 從 3 秒調長到 30 秒或 1 分鐘。不然 Lambda 還在努力幫你壓圖,結果 AWS 一刀切斷:「時間到,下班啦!」你只會得到一個半成品。
現在我們要給 Lambda 權限,不然它進 S3 拿檔案時會被警衛擋下來:「你誰?沒證件別想進!」
用 inline policy 的方式,幫 Lambda 使用的 role 掛上以下權限:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:GetObjectVersion"
],
"Resource": "*"
}
]
}
這份權限其實已經夠大方了(說白話就是:能拿能放,幾乎自由出入)。如果公司資安部門比較龜毛,你還可以再細分 resource 目標,限制它只能動指定 bucket。
總之,沒有這組權限,Lambda 會慘遭拒絕,檔案拿不到、結果寫不回去,你會在 log 裡看到滿滿的「AccessDenied」—— 那畫面比 Timeout 更心碎。
現在劇情來到 S3 bucket 的舞台,我們要讓它成為一個「多管閒事」的鄰居,只要有人把 JPG 丟進來,它就馬上打小報告叫 Lambda 出來搬磚。
首先,請進入 WebImage 這個 bucket 的設定頁,找到 Properties 分頁。
然後滑到中間會看到一個叫做 Event Notifications 的區塊。這名字聽起來就像八卦群組通知一樣,只要有人丟檔,它就會立刻「叮咚」通知。這裡按下 Create Event Notification,開始進行設定。
接下來就是填寫各種小劇本:
workingFileUploadEvent
working/
.jpg
ProcessImage
(就是我們前面做好的那一個)。完成設定後,再回到 Lambda 的主畫面,你會發現多了一個 S3 圖示優雅串起來。這畫面看起來就像「JPG 一上傳 → Lambda 馬上衝出來幫你處理」的自動化流水線。
簡單來說,S3 負責打小報告,Lambda 負責幹活,你就只要躺著喝咖啡。
萬事俱備,只欠「小白鼠」。
我們決定拿一張 1536×1024 的 photo.jpg 來當實驗品,直接丟進 S3 bucket。
檔案一上傳完成,S3 馬上回你一句:「OK,我收到了!」
接著,我們跑去 Ready 目錄一看,哇靠!檔案真的乖乖出現了,而且還瘦身成功。
原本 2.5MB 的巨嬰,現在只剩下 83.6KB,簡直比健身教練還專業。
解析度也從 1536×1024 收斂到 1024×768,看得出來 Lambda 沒偷懶,確實幫我們把任務完成了。
這下厲害了,有了這套流程,之後你就可以把一大堆檔案丟進去,Lambda 會自動幫你「壓縮+轉檔」,效率比菜鳥工程師快一百倍。
一魚兩吃、一次搞定!
這樣學會了嗎?
至於更高深的招數?嘿嘿,就留給各位自己領悟啦~