更改存放部落格圖片的 S3 Bucket 名稱
最近沒事想找事做,想更改用來放置部落格圖片 S3 bucket 的名稱,若要問為什麼的話…
原來部落格的網域是使用 recodeblog.com
,後來覺得不夠帥改成了 docfunc.com
,雖然部落格網域已更改,但卻沒有更改原本用來放置文章圖片的 S3 bucket 名稱 recode-blog-files
。
雖然沒更改並不影響使用,但除了覺得不夠帥之外,我也發現幾年前自己給部落格上傳圖片用的 IAM (Identity Access Management) 帳號權限好像太大了,居然是 S3FullAccess
(那個時候還不是很熟 AWS,雖然現在也沒多熟)。
最近因為工作緣故需要與 AWS 混熟,因此想要藉此機會稍微做個練習,將 Bucket 名稱與 IAM 權限一起調整一下。
建立新的 Bucket、Policy 與角色
S3 的 bucket 只要建立後就無法更改地區與名稱,因此只能建立一個新 Bucket,再將舊 Bucket 的檔案複製過去。
首先先建立一個新的 S3 bucket docfunc-files
,基本上只要輸入名稱與選好地區即可,其他設定都可以先使用預設值。
接下來我們建立一個新的 Policy,這個 Policy 只有上傳檔案至 docfunc-files
的功能,也就是行為 (Action) 只允許 PutObject
,而可以操作的資源 (Resource) 是 docfunc-files
。
設定完成之後的 Policy JSON 如下。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Upload file to blog archives bucket",
"Effect": "Allow",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::docfunc-files/*"
}
]
}
設定好 Policy 之後就可以新增一個角色 blog_file_upload
,並將剛剛新增好的 Policy 掛上這個角色,並產出這個角色的 Access key 與 Secret key,用來給程式上傳圖片使用。
當角色與 Policy 都設定好,我們先回頭修改一下 docfunc-files
的 Policy,主要有兩點。
- 所有人都可以讀取這個 bucket 的資源 (也就是訪問連結可以看到圖片)。
- 只有
blog_file_upload
這個角色可以上傳圖片至docfunc-files
。
之前 S3 bucket 可以使用 ACL (Access control list,存取控制清單) 來管理 Bucket 中個別檔案 (Object) 的存取權限,但現今 AWS 都建議關閉此功能,改用 Policy 去管理權限,現在新增 Bucket 時,ACL 預設都會關閉。
找到 Bucket 的 Permission,並編輯 Policy 的設定。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Allow get requests originating from everyone",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::docfunc-files/*"
},
{
"Sid": "Only allow blog_file_upload to put object",
"Effect": "Allow",
"Principal": {
"AWS": "這裡請輸入指定角色的 ARN 代碼"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::docfunc-files/*"
}
]
}
複製檔案到新的 Bucket
接下來就是將原本 recode-blog-files
中的所有圖片複製一份至新的 docfunc-files
,這裡我使用 AWS CLI 來進行複製,AWS CLI 的安裝可以參考官網的教學,如果是 Mac 的用戶,可以使用 Homebrew 的指令安裝。
brew install awscli
安裝好之後,可以確認一下是否安裝成功與版本是否正確 (建議安裝 version 2)。
aws --version
使用 AWS CLI 之前,必須要先設定給 AWS CLI 使用的 IAM 帳號。
如果是本地端就直接使用具有最高權限
AdministratorAccess
的 IAM 帳號吧,但正式環境請避免。
# 按照指令設定 Access key、Secret key、地區與 output 格式
aws configure
設定完成之後,AWS CLI 就會在 ~/.aws
底下新增 config
與 credentials
這兩個檔案來存放剛剛設定的 Key。
如果你有使用 Terraform 的話,Terraform 在使用 AWS provider 時也是讀取這裡的 Key。
接下來就是使用 AWS CLI 的指令來進行複製。
aws s3 sync s3://recode-blog-files s3://docfunc-files
等待複製程序結束,可以上去 AWS console 查看檔案室否複製成功,或是使用指令查看。
aws s3 ls s3://docfunc-files --recursive --human-readable --summarize
--recursive
:用遞迴的方式反覆查找 Bucket 底下的資料夾並列出所有檔案。--human-readable
:檔案的大小會透過 KB 與 MB 的方式顯示,而不是顯示 byte。--summarize
:顯示所有檔案的數量與總容量。
修改資料庫中所有文章中圖片的連結
文章數量不到 100 篇,因此就直接使用 SQL 語法來更新文章中圖片的連結吧。
⚠️ 請先備份資料防止意外發生。
-- MySQL 8
UPDATE posts
SET body = REPLACE(body, 'https://recode-blog-files', 'https://docfunc-files')
WHERE INSTR(body, 'https://recode-blog-files') > 0;
-- PostgreSQL 16
UPDATE posts
SET body = REPLACE(body, 'https://recode-blog-files', 'https://docfunc-files')
WHERE body LIKE '%https://recode-blog-files%';
語法非常簡單,使用 MySQL 提供的 INSTR()
函式,就可以將文章中有圖片連結的文章資料撈取出來,接下來再用 REPLACE()
函式,將文章中所有 https://recode-blog-files
都換成https://docfunc-files
。
Laravel 程式碼中的調整
除了更新 .env
中的 AWS 的 Access key 與 Secret key,原本程式碼在上傳檔案的時候,還有設定 ACL 權限。
// 原本 put() 最後一個參數有設定 'public',用來調整 ACL 權限
Storage::disk('s3')->put($filePath, file_get_contents($file), 'public');
但因為 ACL 已經停用,而且角色的 Policy 只允許上傳,沒有允許調整 ACL,因此這裡加上 public 參數會出現問題,需要移除。
// 因為 Bucket 改用 Policy 控制權限
// 就算沒有設定 'public',所有人依然能讀取 bucket 中的資源
Storage::disk('s3')->put($filePath, file_get_contents($file));
大功告成!訪問一下文章頁面,查看圖片連結是否已更改還有能否正常顯示。