記一次把部落格圖片從 Imgur 搬到 Cloudflare R2 的過程
在上個月的 Imgur 一直 temporarily over capacity 嗎?先檢查網路看看吧 提到,因為 Imgur 不給上傳圖片了,現在寫個文章還得開 VPN 才能貼圖片。
拖著拖著七月也要過了,決定趁休假的時候來把圖床搬一搬。
比起實作上的拖延,搬家目標則早早決定好要嘗試這篇「Imgur 封鎖台灣 IP,我把圖床搬到 Cloudflare R2 - Code and Me」提到的 Cloudflare R2。
畢竟出口流量免費實在太香了,不愧是賽博菩薩。這就馬上前來皈依。
考慮到每位朋朋的部落格選型不同,先說明一下我家的狀況:
- 使用 Hugo 建置,直接丟在 Github Pages
- 圖片目前放在 Imgur
這代表有些較為暴力的操作可能只適用於我目前的狀況,例如直接在檔案裡搜尋 imgur
來抓圖等等(所以如果你是從關鍵字搜尋進來這篇文章的,請從右手邊的文章目錄直接前往你需要的小節)
這次圖床搬家主要進行了以下步驟:
- 建立 Cloudflare R2 資源
- 寫個 Powershell 腳本把目前部落格放在 Imgur 的圖片都抓下來
- 順便用 WebP 壓縮一下圖片
- 把圖片丟到 Cloudflare R2
- 回來取代文章內的圖片連結
- 使用 VSCode 的 Markdown-image 套件直接上傳圖片到 Cloudflare R2
如此如此,說搬就搬,這篇就紀錄一下這次的圖床搬家過程。
建立 Cloudflare R2 資源
既然都決定要搬家到 Cloudflare R2 了,首先當然是要建立一個 Cloudflare R2 資源。
建立 R2 的操作可以參考這篇「架設 Cloudflare R2 免費圖床,給 Hugo 靜態網站託管圖片 - Ivon 的部落格」的「新增 Cloudflare R2 bucket」小節,流程詳細還附圖,基本可以照著把 R2 儲存空間給建出來。
總之先到 Cloudflare 儀表板,把 R2 儲存體建出來,然後順手掛個自訂網域:
備註:如果想要自訂 R2 資源對外的網址,需要先讓 R2 和 Cloudflare 上存在的網域做綁定,也就是要在 Cloudflare 買網域或是其他地方買來掛在 Cloudflare 上(參考 Public buckets)
因為我(之前為了改 Bluesky 的帳號名稱)已經在 Cloudflare 購買過網域了,所以可以直接用自定義網域來把圖片網址改成我要的網址。
如果你也想要自訂圖片網址,但在 Cloudflare 還沒有任何網域,可以先多找幾篇網域購買和託管到 Cloudflare 的文章(例如 Cloudflare 網域購買教學)參考參考,再決定要不要投入 $$ 買一個自己喜歡的名字。
建立完畢之後,可以先進貯體隨便上傳一張圖片,確認一下 URL 有沒有連到圖片:
弄個 Powershell 腳本把部落格的圖片先抓下來
建好 R2 空間之後,就要開始著手搬遷啦~
我的部落格採用 Hugo + Github Pages,所以文章內容也就是一堆 Markdown 檔案而已。這種狀況下,最快的方式就是直接搜出文章中的圖片連結就行。
幸好 2025 年的懶人是有福的,直接請 GPT 幫我產一組 Powershell 腳本,把 .md
檔拖出來,掃到圖片就抓下來存著,迅速搞定這一階段:
# see: https://gist.github.com/mufidu/f7b795f844f1ee4dc78e55123d5a398b
# 1. 指定資料夾
$folder = "C:\Blog\content"
$outputDir = "C:\Users\xxx\Pictures\Blog-Image-Backup"
# 2. 建立目標資料夾(若不存在)
if (-not (Test-Path $outputDir)) {
New-Item -Path $outputDir -ItemType Directory | Out-Null
}
# 3. 逐一掃描 .md 檔案
Get-ChildItem -Path $folder -Filter *.md -Recurse | ForEach-Object {
$mdFile = $_.FullName
$content = Get-Content -Path $mdFile
foreach ($line in $content) {
# 4. 正則擷取 Markdown 圖片 URL
if ($line -match '!\[[^\]]*\]\((?<url>https?://[^\)]+)\)') {
$url = $Matches['url']
# 5. 以 URL 最後一節當作檔名
$fileName = Split-Path -Path $url -Leaf
$destPath = Join-Path $outputDir $fileName
try {
# 6. 下載圖片
Invoke-WebRequest -Uri $url -OutFile $destPath -UseBasicParsing
Write-Host "已下載: $url → $destPath"
}
catch {
Write-Warning "下載失敗: $url (`$_`)"
}
}
}
}
插件:順便用 WebP 把圖片做一下壓縮
圖片下載完後,突然想起之前跑 PageSpeed Insights 時有被提示過圖檔太大的問題,決定趁這機會順便把圖壓成 WebP。
WebP 是 Google 推的圖片格式,從 developers.google 對 WebP 的介紹 來看,WebP 無損壓縮後的檔案大小能比 PNG 小 26%,而有損壓縮在同樣壓縮品下也能比 JPEG 小 25-34%。
補充:有些情況下跑完 WebP 反而會讓檔案大小變大,例如 JPG 轉 WebP(參見 Image size is increased when converted from jpg to webp with quality value 100)
而在 WebP 常見問題文檔的「Can a WebP image grow larger than its source image?」這一小節有說明可能會導致檔案變大的場景,有遇到的朋友可以先確認看看。
因為我的部落格大多是 PNG 圖檔,文檔提到「Note that converting a JPEG source to lossy WebP, or a PNG source to lossless WebP are not prone to such file size surprises.」所以我仍然可以無腦轉 WebP,如果是跟我一樣都是 PNG 的朋友就別擔心了。
在開始動手之前,需要先確保環境中有 WebP 的工具。沒有的朋朋們請先到 Google for Developers 下載回來(為了方便後續使用,可以把 bin
的路徑丟進環境變數)
接著同樣產個小腳本來逐個把圖片壓成 WebP,這段除了好朋友 GPT 以外,還參考了這兩篇:
- 透過 PowerShell 製作 JPG/ PNG 轉 webp 小工具 (使用 Google webp converter lib) | Mark Ku’s Blog
- 用命令行一次壓縮多張 WebP 照片 - HY - Medium
# 記得先下載 WebP 工具: https://developers.google.com/speed/webp/download
# 1. 設定來源與輸出資料夾
$inputFolder = "C:\Users\xxx\Pictures\Blog-Image-Backup"
$outputFolder = Join-Path $inputFolder "WebP"
# 2. 建立輸出資料夾(若不存在)
if (-not (Test-Path $outputFolder)) {
New-Item -Path $outputFolder -ItemType Directory | Out-Null
}
# 3. 定義可轉檔副檔名清單
$convertExts = @('.jpg', '.jpeg', '.png')
# 4. 遞迴取得所有檔案,並排除輸出資料夾本身
Get-ChildItem -Path $inputFolder -File -Recurse |
Where-Object { $_.FullName -notlike "$outputFolder*"} |
ForEach-Object {
$src = $_.FullName
$ext = $_.Extension.ToLower()
if ($convertExts -contains $ext) {
# 5a. 轉檔:jpg/jpeg/png → webp
$dest = Join-Path $outputFolder ($_.BaseName + ".webp")
# 使用 -q 指定壓縮品質(0-100, ex: -q 80)
# 如果需要無損壓縮,可以使用 -lossless 參數
# 但注意有損的 JPG 之類轉無損 WebP 反而會讓檔案變大哦
& cwebp -lossless $src -o $dest > $null
Write-Host "Converted: $src → $dest"
}
else {
# 5b. 其餘格式(例如 gif)直接複製
# 畢竟後續還是都要上傳到新的圖床,不能轉的就直接過去吧
$dest = Join-Path $outputFolder $_.Name
Copy-Item -Path $src -Destination $dest -Force
Write-Host "Copied: $src → $dest"
}
}
上傳圖片到 Cloudflare R2
現在我們有一卡車圖檔準備要大舉進攻 Cloudflare R2,按照上面的節奏。聰明的朋朋也許會想:
這傢伙又要叫 GPT 幫我們產腳本來上傳了吧?
但很可惜,我請 GPT 產腳本是因為我很懶,所以有更懶的方法存在時,我是連腳本都不考慮的。
這個步驟最懶的方法就是直接滑鼠點一點,畢竟 Cloudflare R2 在瀏覽器就可以拖檔案上傳了:
雖然瀏覽器上傳有每次一百個檔案的限制,但考慮到我部落格 拖稿嚴重 圖片不多,因此手動上傳也是分分鐘的事情,這個步驟就手動傳一傳收工。
補充:如果檔案太多,或是好想用下指令的方式上傳呢?請參考 R2 文檔的 Upload objects
因為前面的步驟只是下載檔案又上傳檔案,連檔名都沒更動,接著只要回去文章內文把原本有 i.imgur.com
的連結都替代成新的圖片網址就搞定囉~
補充:如果像我一樣,有些 JS 的操作會去呼叫圖片網址的話,可能會看到 CORS 的錯誤訊息,例如:
Access to image at 'https://xxxx/xxxxxx.webp' from origin 'https://xxxxxx.github.io' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
這時候只要去 R2 儲存體的「設定」 找到 CORS 原則,把站台加上去就可以了。
使用 VSCode 的 Markdown-image 延伸模組直接上傳圖片到 Cloudflare R2
到這邊其實部落格的圖床遷移已經完成了,但因為我習慣用 VSCode 撰寫文章,之前都用 vscode-imgur 這款延伸模組,實現貼上圖片自動上傳 Imgur 的舒適體驗。
但現在圖床搬家到 Cloudflare R2 了,當然也要調整一下 VSCode 的上傳圖片工具。果斷採用開頭這篇「Imgur 封鎖台灣 IP,我把圖床搬到 Cloudflare R2」提到的 Markdown-image
另外在設定的過程中還參考了以下兩篇:
首先我們得申請一組權杖,可以參考 Cloudflare 官方文檔的 Authentication 操作,或是從 R2 頁面的右側進入 API 權杖的頁面:
新增一組能夠「物件讀取和寫入」的權杖後,會來到金鑰畫面。注意這組金鑰跟識別碼只會出現一次,請務必記下來,等等設定延伸模組的時候會用到:
建立完之後,進到我們要上傳圖片的貯體,進到設定頁面,這邊的名稱跟 S3 API 資訊等等也會用到:
接著讓我們回到 VSCode,到設定裡面找到 Markdown Image 的相關設定,把上傳圖片的方式改為「S3」(不是 Cloudflare 哦!)
最後就是把剛剛拿到的資訊一個一個填進去設定裡啦~
這邊提供我填的內容給需要的朋朋參考:
- S3: Endpoint => R2 設定頁面的 S3 API
- ps: 可以不用放最後面的儲存體名稱,反正等等 Bucket Name 要放
- S3: Region => 直接填 auto 就好
- S3: Bucket Name => 儲存體(貯體)名稱,抄 R2 設定頁面的名稱就好
- ps: 如果 Endpoint 有包含儲存體名稱了,這邊會開成資料夾
- S3: Access Key ID => 發金鑰時拿到的 存取金鑰識別碼
- S3: Secret Access Key => 發金鑰時拿到的 秘密存取金鑰
- S3: Cdn => 我們自定義的 CDN 路徑 + {filepath}"
都搞定之後就可以開個 .md
檔、複製個圖片,試試看 Shift + Alt + V
有沒有自動上傳囉~
小結
從 Imgur 爆炸到搬完圖片也拖了快兩個月,但幸好有蠻多前人遺跡和好夥伴 GPT 的協助,還算搬得順利。秉持著一個「做都做了」的理念,乾脆把圖床搬家過程也拿出來水一篇,喜得部落格文章數 +1
搬完之後想說圖片也壓縮了、R2 也有快取之類的,是不是再來跑一次 PageSpeed Insights,分數意外地比之前高不少,也算是一個意外撿到的驚喜了
參考資料
- Imgur 封鎖台灣 IP,我把圖床搬到 Cloudflare R2 - Code and Me
- 使用 Cloudflare R2 當作遠端圖床 - Eyewithouts 韋觀
- 架設 Cloudflare R2 免費圖床,給 Hugo 靜態網站託管圖片 · Ivon的部落格
- 透過 PowerShell 製作 JPG/ PNG 轉 webp 小工具 (使用 Google webp converter lib) | Mark Ku’s Blog
- 用命令行一次壓縮多張 WebP 照片 - HY - Medium
- What Is a WebP File? How WebP Compares To JPEG and PNG (2025) - Shopify
- 使用 Markdown Image 扩展实现上传图片到 Cloudflare R2
- Hugo vscode 自动上传图片到cloudflare | Hao DevSecOps
- Authentication · Cloudflare R2 docs
- Upload objects · Cloudflare R2 docs
- Custom domain for R2 bucket not hosted on Cloudflare - Developers / Images - Cloudflare Community
哈囉,如果你也有 LikeCoin,也覺得我的文章有幫上忙的話,還請不吝給我拍拍手呦,謝謝~ ;)