更改存放部落格圖片的 S3 Bucket 名稱

程式技術
sharkHead
更改存放部落格圖片的 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,基本上只要輸入名稱與選好地區即可,其他設定都可以先使用預設值。

2022_07_31_22_24_49_62e690b1c9cbc.png

接下來我們建立一個新的 Policy,這個 Policy 只有上傳檔案至 docfunc-files 的功能,也就是行為 (Action) 只允許 PutObject,而可以操作的資源 (Resource) 是 docfunc-files

2022_07_31_22_26_15_62e6910797eb7.png
S3 光是 write 權限,就有多到令人眼花撩亂的選項

設定完成之後的 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 底下新增 configcredentials 這兩個檔案來存放剛剛設定的 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;

語法非常簡單,使用 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));

大功告成!訪問一下文章頁面,查看圖片連結是否已更改還有能否正常顯示。

參考資料

sharkHead
written by
sharkHead

後端打工仔,在下班後喜歡研究各種不同的技術。稍微擅長 PHP,並偶爾涉獵前端開發。個性就像動態語言般隨興,但渴望做事能像囉嗦的靜態語言那樣嚴謹。

0 則留言