iT邦幫忙

2025 iThome 鐵人賽

DAY 24
0

透過 EventBridge 做定時操作前面我們玩過了,像是幫 EC2 上下班打卡的那種。今天要來點新花樣:不是定時,而是「有事才找人」的那種觸發事件。

說白一點,就是 S3 幫你當主管,Event 當小秘書,Lambda 當實習生。流程是這樣:

  • 你丟一張圖進 S3 的 working 資料夾。
  • S3 馬上大喊:「有貨啦!」 → Event 發出通報。
  • Event 再拍拍實習生 Lambda 的肩膀:「喂,你的活兒來了!」
  • Lambda 忍著沒加班費的心酸,把原本肥滋滋的 4K 圖檔壓成 1024×768,然後乖乖放回 ready/ 資料夾。

這樣一來,網站使用者就不用再被 2.5MB 的大圖炸流量,變成 80KB 的小清新,瀏覽體驗直線上升。工程師也不用再打開 Photoshop 一張一張手工壓圖,省下來的時間剛好可以拿去滑手機、喝咖啡,或者研究怎麼讓 Lambda 幫自己洗衣服(可惜做不到)。

整條 AWS 縮圖流水線就像工廠:

  • S3:倉庫 → 收貨還會喊人。
  • Event:小秘書 → 幫忙轉通知。
  • Lambda:打工仔 → 沒薪水但超有效率。
  • 一句話總結:上傳 → 事件觸發 → 自動加工 → 新檔出爐。

架構圖如下(沒圖不專業,但就算有圖你也還是得自己設定):
https://ithelp.ithome.com.tw/upload/images/20251004/20141071DmYRGlJm2M.png

接下來,我們就要正式把這條「AWS 自動壓圖生產線」組起來啦

先準備一個 S3 Bucket

一切從 S3 Bucket 開始,畢竟在 AWS 的世界裡,沒有桶子你就啥都放不了。所以第一步:開一個 S3 bucket。

我們取一個 webimage + 亂碼風格名字:
webimage-430299fd-d75a-46e5
(看起來就像是喝醉後亂按鍵盤的作品,但至少保證不會撞名 😏)

接著,我們在這個 bucket 裡分兩個小房間:

  • working/肥圖專區,所有剛上傳、還沒減肥的高解析度圖檔都先丟這裡。
  • ready/修圖完畢 VIP 區,所有被 Lambda 壓到 1024×768 的瘦身成功作品會乖乖放這邊。

其他設定?別想太多,全都用預設值就好。工程師的座右銘就是:能不改設定就不改,因為多改一個選項,多一個坑等你跳。

https://ithelp.ithome.com.tw/upload/images/20251004/20141071ZMRg7pEfSf.png

建立 Lambda

好了,時間到了,把「肥滋滋的大圖」交給我們的無名英雄 —— Lambda。流程很簡單也很簡單:使用者把 jpg 丟到 webimage-430299fd-d75a-46e5/working/,Lambda 抓去做身材管理,最後把瘦身成功的成品丟到 webimage-430299fd-d75a-46e5/ready/

簡單一句話:上傳 → Lambda 出馬 → 壓成 1024×768 → 放到 ready。工程師只要坐等成果,不用再一張一張手動按存檔。

小提醒:我們的範例只專攻 jpg(也就是 JPG 的專屬健身房)。要是你想讓 PNG、WEBP、GIF 也來報到,程式碼跟相依套件可能要一起升級、改寫、做些小動作 —— 就像不同體型的人做不同熱身一樣。

下面請用這些參數建立一個 Lambda:

  • Function NameprocessImage
  • Runtime:Python 3.13
  • Architecture:x86_64
  • 程式碼
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

加入 Layer(不然 Lambda 會直接躺平)

別忘了,我們的程式裡頭有一段:from PIL import Image

這意思是什麼?就是 Lambda 原廠沒附這個零件,結果你叫它處理圖片,它只會傻眼回你:「哥,我光靠原廠附贈的功能,只能幫你印 Hello World,修圖這種高級服務請自己帶工具來。」

解法很簡單 — 把 Pillow 打包成一個 Layer,再幫 Lambda 加掛,就像替打工仔配一副 Photoshop 套件,不然它連圖片都打不開。

打開 Lambda 的設定頁面,你會看到「Layers」那一小塊。點下去,然後… 一直往下滑,才會看到它躲在最下面。
https://ithelp.ithome.com.tw/upload/images/20251004/201410711eD9UNpUsx.png

接著按右上角的 Add a layer,進入新畫面。
https://ithelp.ithome.com.tw/upload/images/20251004/20141071qdW9xbUzc9.png

這裡選 Custom Layers → 下拉選單挑 PY313-pillow(這就是我們上篇 DIY 包好的 Pillow 套件)。記得挑最新版本,舊版本就像過期牛奶,誰敢喝?然後點 Add
https://ithelp.ithome.com.tw/upload/images/20251004/20141071yJIpVnEFkR.png

搞定後,你會看到 Pillow 已經掛到 Lambda 身上了,這時候它終於有能力幫你處理圖片了。
https://ithelp.ithome.com.tw/upload/images/20251004/20141071CoDjK23YF0.png
https://ithelp.ithome.com.tw/upload/images/20251004/20141071GXSVwjLmYq.png

最後提醒一個大坑:把 Lambda 的 Timeout 從 3 秒調長到 30 秒或 1 分鐘。不然 Lambda 還在努力幫你壓圖,結果 AWS 一刀切斷:「時間到,下班啦!」你只會得到一個半成品。
https://ithelp.ithome.com.tw/upload/images/20251004/20141071eSmvRd7sz8.png

權限設定(給 Lambda 一張通行證,不然它進不去倉庫)

現在我們要給 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 event 的設定

現在劇情來到 S3 bucket 的舞台,我們要讓它成為一個「多管閒事」的鄰居,只要有人把 JPG 丟進來,它就馬上打小報告叫 Lambda 出來搬磚。

首先,請進入 WebImage 這個 bucket 的設定頁,找到 Properties 分頁。
https://ithelp.ithome.com.tw/upload/images/20251004/20141071UZPWgyxH82.png

然後滑到中間會看到一個叫做 Event Notifications 的區塊。這名字聽起來就像八卦群組通知一樣,只要有人丟檔,它就會立刻「叮咚」通知。這裡按下 Create Event Notification,開始進行設定。
https://ithelp.ithome.com.tw/upload/images/20251004/20141071jtBJPMFdrg.png

接下來就是填寫各種小劇本:

  1. Event NameworkingFileUploadEvent
    請取一個自己看得懂的名字,不然三天後回來 debug,會懷疑是不是隔壁組同事惡搞你。
  2. Prefixworking/
    就像門牌號碼,告訴 S3 只要監控這個目錄下的東西。
  3. Suffix.jpg
    我們這次只針對 JPG。其他格式?不好意思,這場派對你沒受邀。
  4. All object create events:選它。
    意思就是「只要有人新丟檔案上來,不管用什麼姿勢(Console、CLI、SDK…),我都要告訴 Lambda」。

https://ithelp.ithome.com.tw/upload/images/20251004/20141071lx5EhvKxTy.png

  1. Destination:Lambda function
    就是選擇「誰要出來收通知」。答案當然是 Lambda,不然要通知你老闆嗎?
  2. Choose from your Lambda functions
    勾起來,因為我們要自己點名誰要來搬 JPG。
  3. 下拉選單裡挑剛剛辛苦養大的 ProcessImage(就是我們前面做好的那一個)。
    Lambda 就像剛被 assign 任務的實習生,被叫名字後只能硬著頭皮接下來。
  4. 按下右下角的 Save Changes
    這一步超重要,沒存的話…呵呵,等你測試一整天都沒反應時再來哭。

完成設定後,再回到 Lambda 的主畫面,你會發現多了一個 S3 圖示優雅串起來。這畫面看起來就像「JPG 一上傳 → Lambda 馬上衝出來幫你處理」的自動化流水線。
https://ithelp.ithome.com.tw/upload/images/20251004/201410711NwgNfToE8.png

簡單來說,S3 負責打小報告,Lambda 負責幹活,你就只要躺著喝咖啡。

測試時間到了

萬事俱備,只欠「小白鼠」。
我們決定拿一張 1536×1024 的 photo.jpg 來當實驗品,直接丟進 S3 bucket。
https://ithelp.ithome.com.tw/upload/images/20251004/20141071DG8Y7sb6JR.png

檔案一上傳完成,S3 馬上回你一句:「OK,我收到了!」
https://ithelp.ithome.com.tw/upload/images/20251004/20141071SsTX0QJxIs.png

接著,我們跑去 Ready 目錄一看,哇靠!檔案真的乖乖出現了,而且還瘦身成功。
原本 2.5MB 的巨嬰,現在只剩下 83.6KB,簡直比健身教練還專業。
https://ithelp.ithome.com.tw/upload/images/20251004/20141071syoNtOGE5G.png

解析度也從 1536×1024 收斂到 1024×768,看得出來 Lambda 沒偷懶,確實幫我們把任務完成了。
https://ithelp.ithome.com.tw/upload/images/20251004/201410719HiSya4OpI.png

這下厲害了,有了這套流程,之後你就可以把一大堆檔案丟進去,Lambda 會自動幫你「壓縮+轉檔」,效率比菜鳥工程師快一百倍。

  • 手機用戶:看到精簡版圖片,感覺速度快到像換了 5G。
  • 電腦用戶:拿到高解析度圖片,瀏覽體驗就像開了 27 吋螢幕在爽看。

一魚兩吃、一次搞定!

這樣學會了嗎?
至於更高深的招數?嘿嘿,就留給各位自己領悟啦~


上一篇
Day 23 - 用 AWS Lambda Layer 管 Python 套件
下一篇
Day 25 - Amazon API Gateway 雲端界的 API 大門
系列文
最適合小型工作室精打細算的服務使用法25
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言