上傳照片或檔案是應用程式中基本的功能之一,如果後端是用 Firebase 類的服務的話那是有現成的 API 和 Storage 可以用,但如果要自己從頭做起的話 AWS S3 就是一個常見的選擇,而透過 presigned url 上傳是其中一種方式。
Requirement
- Create an AWS S3 presigned URL
- Upload an image through the URL
1. Prerequisite
Create presigned URL 其實並不需要 permission,是在使用 URL 的時候才會被檢查用來 create URL 的 credential 在當下是否有 presigned action 的 permission,不過在這邊我們還是先把需要的 credential、permission 和 resource 都建好。
1–1. Create an IAM user: practices-bc-01
首先我們建立一個 IAM User,不給他任何 permission。
1-2. Create a policy with required permissions: practices-bc-01
這邊沒有要嘗試 fine tune permissions,所以直接開了個等同於 AmazonS3FullAccess 的 Policy。
1-3. Grant the user the permission
為了讓 user 的 credential 有上傳 object 到 S3 的 permission,我們把剛剛 create 的 policy 加到 user 身上。
1-4. Create a S3 bucket: practices-bc-01
最後再 create 一個 S3 bucket 當作照片上傳的地方,bucket 是有分 region 的,記得在這邊選的 region 必須要和 create presigned URL 時用的一樣。
Bucket 的 permission 也有影響:
- Block public access to buckets and objects granted through new access control lists (ACLs) 需要打開,在使用 presigned URL 時才不會 Access Denied。
- Block public access to buckets and objects granted through any access control lists (ACLs) 需要打開,上傳照片的 ACL 才能設定為 public-read。
2. Create the presigned URL
準備好 credential、permission 和 resource 後,我們就可以用 Node.js 和 aws-sdk 來 create presigned URL 了。
// .env
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_REGION=// src/create-url.js
require("dotenv").config();
const fs = require("fs");
const AWS = require("aws-sdk");
const s3 = new AWS.S3({
apiVersion: "2006-03-01"
});
var params = {
Bucket: "practices-bc-01",
Key: "test.jpg",
ACL: "public-read",
ContentType: "image/jpeg"
};
var url = s3.getSignedUrl("putObject", params);
fs.writeFileSync("src/url.txt", url);// src/url.txt
https://practices-bc-01.s3.ap-northeast-1.amazonaws.com/test.jpg?AWSAccessKeyId=AKIAXQN5DOMFBQJ3MTWB&Content-Type=image%2Fjpeg&Expires=1560079674&Signature=RVVfTM6OcVp832%2F9%2BujX%2BRss2ZI%3D&x-amz-acl=public-read
3. Upload image through cURL
最後我們用 cURL 將一張 test.jpg 的照片透過 presigned URL 上傳到 S3。
#!/bin/sh
curl -X "PUT" \
-H "Content-Type: image/jpeg" \
-T "$(pwd)/src/test.jpg" \
-L $(cat "$(pwd)/src/url.txt")
成功!