iT邦幫忙

2025 iThome 鐵人賽

DAY 25
0
Build on AWS

從零到雲端:AWS 開發之路系列 第 25

Day 25: 雲端收納 ─ Lambda 幫我把照片存進 S3

  • 分享至 

  • xImage
  •  

功能目標

  • 使用前端網頁選擇照片
  • 點擊上傳,透過 Lambda 生成 S3 預簽名 URL
  • 使用 PUT 將照片上傳到 S3
  • 上傳成功後可以在前端看到照片或儲存 URL

S3 設定

建立 S3 bucket,例如:photo-diary-frontend。
開啟 Public access(僅示範可讀,正式建議使用私有)。
在 Permissions 中的 Bucket Policy 中加入:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowLambdaUpload",
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::photo-diary-frontend/*"
    },
    {
      "Sid": "AllowPublicRead",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::photo-diary-frontend/*"
    }
  ]
}

Lambda 需要 s3:PutObject 權限,前端可以用 GET 讀取圖片。

Lambda 函數

語言:Python 3.9
功能:生成 S3 預簽名 URL

import json
import boto3
from botocore.exceptions import ClientError

S3_BUCKET = "photo-diary-frontend"
s3_client = boto3.client("s3", region_name="ap-east-2")

def lambda_handler(event, context):
    # CORS 預檢
    if event.get("httpMethod") == "OPTIONS":
        return {
            "statusCode": 200,
            "headers": {
                "Access-Control-Allow-Origin": "*",
                "Access-Control-Allow-Headers": "*",
                "Access-Control-Allow-Methods": "POST,GET,PUT,OPTIONS"
            },
            "body": ""
        }

    # POST:生成預簽名 URL
    if event.get("httpMethod") == "POST":
        try:
            body = json.loads(event["body"])
            filename = body.get("filename")
            if not filename:
                return {
                    "statusCode": 400,
                    "headers": {"Access-Control-Allow-Origin": "*"},
                    "body": json.dumps({"message": "Missing filename"})
                }

            upload_url = s3_client.generate_presigned_url(
                "put_object",
                Params={
                    "Bucket": S3_BUCKET,
                    "Key": f"photos/{filename}",
                    "ContentType": "image/jpeg"
                },
                ExpiresIn=300  # 5 分鐘
            )

            return {
                "statusCode": 200,
                "headers": {"Access-Control-Allow-Origin": "*"},
                "body": json.dumps({"uploadURL": upload_url})
            }

        except ClientError as e:
            print(e)
            return {
                "statusCode": 500,
                "headers": {"Access-Control-Allow-Origin": "*"},
                "body": json.dumps({"message": "Internal Server Error"})
            }

    return {
        "statusCode": 405,
        "headers": {"Access-Control-Allow-Origin": "*"},
        "body": json.dumps({"message": "Method Not Allowed"})
    }

前端流程說明

使用 <input type="file"> 選擇照片
預覽照片:FileReader.readAsDataURL()
按下「上傳」後:

  • 發 POST 請求到 Lambda,取得 S3 預簽名 URL
  • 再用 PUT 將照片上傳到 S3

上傳完成後可用 S3 URL 顯示照片

碰到的瓶頸

Access to fetch at ... from origin ... has been blocked by CORS policy
S3 Bucket 設定 CORS 後還是顯示 S3 不允許跨域,預簽名 URL 無法處理 OPTIONS,在S3的CORS中也無法使用OPTIONS,我真的試了快8小時還是一樣...非常崩潰呀/images/emoticon/emoticon02.gif

解決方法

因為時間比較緊迫,我還是想把功能實現,所以想了一下解決方案,就是將照片轉成 Base64 字串,再存到 localStorageIndexedDB,不經過伺服器,也不用 S3 ,完全不會有 CORS 問題,但缺點就是資料全存在前端,雖然即使刷新還是會存在,但是就長期來看,儲存空間其實不太夠,還是比較不建議使啦!那這邊還是會繼續學習,到了能解決這個問題的時候我會再嘗試把功能上到AWS上!那麼明天我們就來看看他是如何存在前端的吧!


上一篇
Day24 回憶需要影像 ─ 打造照片上傳與預覽功能
系列文
從零到雲端:AWS 開發之路25
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言