Skip to content

用 Cloudflare 免費方案建立自己的圖庫,真的 $0 起步

你有沒有想過,把自己常用的免費商用圖片集中管理,建一個自己的圖庫入口,搜尋一下就能找到——而且完全不花錢?

我們做了,這篇文章分享整個架構怎麼搭。


先說為什麼要自己建

做設計、做網站、做行銷,免不了找圖。Pixabay、Pexels、Unsplash 都很好用,但每次都要各自打開、各自搜尋,時間碎片化。

更煩的是:你用過一張圖,下次根本不記得在哪找到的。

如果能有一個自己的圖庫入口,把常用來源全部整合進去,搜尋一次橫掃所有來源,那才是省時間的方式。

這就是我們做 FreePicHub 自由圖匯 的出發點。


Cloudflare 免費額度有多夠用

很多人不知道 Cloudflare 有多慷慨。以下是這個專案實際用到的服務,全部在免費方案內:

服務用途免費額度
R2 物件儲存存圖片檔案10 GB 儲存 / 每月 1,000 萬次 GET
D1 資料庫存圖片 metadata5 GB 儲存 / 每日 500 萬次讀取
WorkersAPI 後端每日 10 萬次請求
Pages前端網站無限靜態部署

一個小型到中型的個人圖庫,這個額度完全夠用,而且不需要信用卡就能開始。


整體架構

[Python 自動化匯入腳本]

  圖片處理 (WebP 壓縮)

 ┌──────────────────┐
 │    Cloudflare    │
 │                  │
 │  R2 (圖片檔案)   │
 │  D1 (資料庫)     │
 │  Workers (API)   │
 │  Pages (前端)    │
 └──────────────────┘

    瀏覽器使用者

四個 Cloudflare 服務各司其職:

  • R2:放壓縮過的圖片(縮圖 / 預覽圖 / 原圖)
  • D1:SQLite 資料庫,存標題、尺寸、標籤、圖片的 R2 路徑
  • Workers:寫兩支 API — /api/search/api/featured
  • Pages:靜態前端,純 HTML + CSS + JS,無框架依賴

Step 1:建立 R2 Bucket

進 Cloudflare Dashboard → R2 → 建立 Bucket,名稱自訂(例如 my-image-library)。

接著到 Manage R2 API Tokens,建立一個 User API Token:

  • 權限選「Object Read & Write」
  • 限定套用到你的 Bucket

記下 Access Key ID、Secret Access Key、帳號 ID,之後 Python 腳本會用到。

R2 是 S3 相容 API,所以 Python 端用 boto3 就能直接操作,不需要學新的 SDK。


Step 2:建立 D1 資料庫

在 Wrangler CLI 執行:

bash
npx wrangler d1 create my-image-db

記下回傳的 database_id。然後建立 Schema:

sql
CREATE TABLE IF NOT EXISTS images (
  id            TEXT PRIMARY KEY,
  source        TEXT NOT NULL,
  source_id     TEXT,
  title         TEXT,
  tags          TEXT DEFAULT '[]',
  width         INTEGER,
  height        INTEGER,
  aspect_ratio  TEXT,
  license_type  TEXT NOT NULL,
  thumb_key     TEXT,
  preview_key   TEXT,
  original_key  TEXT,
  created_at    TEXT DEFAULT (datetime('now'))
);

CREATE INDEX IF NOT EXISTS idx_source    ON images(source);
CREATE INDEX IF NOT EXISTS idx_created   ON images(created_at DESC);
CREATE INDEX IF NOT EXISTS idx_source_id ON images(source, source_id);
bash
npx wrangler d1 execute my-image-db --file=schema.sql

Step 3:Workers API(用 Pages Functions)

最方便的做法是用 Cloudflare Pages Functions,直接把 JS 放在 /functions 目錄下,Cloudflare 會自動部署成 Worker。

搜尋 API (functions/api/search.js):

javascript
export async function onRequestGet({ request, env }) {
  const url    = new URL(request.url)
  const q      = url.searchParams.get('q') || ''
  const source = url.searchParams.get('source') || ''
  const page   = parseInt(url.searchParams.get('page') || '1')
  const limit  = 24
  const offset = (page - 1) * limit

  let sql    = 'SELECT * FROM images WHERE 1=1'
  let params = []

  if (q) {
    sql += ' AND (title LIKE ? OR tags LIKE ?)'
    params.push(`%${q}%`, `%${q}%`)
  }
  if (source) {
    sql += ' AND source = ?'
    params.push(source)
  }

  sql += ` ORDER BY created_at DESC LIMIT ${limit + 1} OFFSET ${offset}`

  const { results } = await env.MY_DB.prepare(sql).bind(...params).all()
  const hasMore = results.length > limit

  return Response.json({
    results: results.slice(0, limit).map(r => ({
      ...r,
      tags: JSON.parse(r.tags || '[]')
    })),
    has_more: hasMore
  }, { headers: { 'Access-Control-Allow-Origin': '*' } })
}

Step 4:圖片處理與自動化匯入

這是整個系統最有趣的部分。寫一支 Python 腳本,把圖片處理後上傳到 R2,metadata 寫入 D1。

圖片處理:每張圖產出三個版本,全部存成 WebP

python
from PIL import Image

def process_image(source_path, output_dir):
    img = Image.open(source_path).convert("RGB")
    w, h = img.size

    # 縮圖 512px(前端列表用)
    thumb = img.copy()
    thumb.thumbnail((512, 512), Image.LANCZOS)
    thumb.save(output_dir / "thumb.webp", "WEBP", quality=82)

    # 預覽圖 1024px(點開 modal 用)
    preview = img.copy()
    preview.thumbnail((1024, 1024), Image.LANCZOS)
    preview.save(output_dir / "preview.webp", "WEBP", quality=85)

    # 原圖轉 WebP 壓縮後保存
    img.save(output_dir / "original.webp", "WEBP", quality=90)

    return {"width": w, "height": h}

WebP 格式比 JPEG 通常小 25–35%,長期下來 R2 儲存成本差很多。

上傳到 R2(boto3 S3 相容介面)

python
import boto3

s3 = boto3.client(
    "s3",
    endpoint_url=f"https://{ACCOUNT_ID}.r2.cloudflarestorage.com",
    aws_access_key_id=ACCESS_KEY,
    aws_secret_access_key=SECRET_KEY,
    region_name="auto",
)
s3.put_object(Bucket="my-image-library", Key="source/uuid/thumb.webp", Body=file_bytes)

寫入 D1(防止重複寫入的關鍵)

python
import requests, uuid, json

def write_to_d1(record):
    resp = requests.post(
        f"https://api.cloudflare.com/client/v4/accounts/{ACCOUNT_ID}/d1/database/{DB_ID}/query",
        headers={"Authorization": f"Bearer {D1_TOKEN}"},
        json={
            "sql": """
                INSERT INTO images (id, source, source_id, title, tags,
                  width, height, aspect_ratio, license_type,
                  thumb_key, preview_key, original_key)
                SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
                WHERE NOT EXISTS (
                  SELECT 1 FROM images WHERE source=? AND source_id=?
                )
            """,
            "params": [str(uuid.uuid4()), record["source"], record["source_id"],
                       record["title"], json.dumps(record["tags"]),
                       record["width"], record["height"], record["aspect_ratio"],
                       record["license_type"], record["thumb_key"],
                       record["preview_key"], record["original_key"],
                       record["source"], record["source_id"]]
        }
    )
    return resp.json().get("success", False)

WHERE NOT EXISTS 讓你重複執行腳本也不會寫入重複資料——這個設計讓整個系統可以安心地每天定時跑,不用擔心跑了幾次。


Step 5:前端部署

前端三個靜態檔案:index.htmlstyle.cssapp.js,推上 Cloudflare Pages:

bash
npx wrangler pages deploy ./frontend --project-name my-image-library

CSS masonry 瀑布流排版,完全不需要 JS 套件:

css
.image-grid {
  columns: 4 220px;
  gap: 14px;
}
.image-card {
  break-inside: avoid;
  margin-bottom: 14px;
}

成本試算

以每天匯入 500 張、圖庫規模 10,000 張計算(縮圖約 50KB、預覽圖約 200KB):

項目用量費用
R2 儲存≈ 2.5 GB$0
D1 讀取(日訪客 1,000 人)5,000 次/日$0
Workers 請求5,000 次/日$0
Pages 部署無限次$0
總計$0 / 月

Cloudflare 的免費額度對個人或小型商業圖庫來說幾乎用不完。等規模到幾十萬張、日訪客破萬,再評估要不要升級方案。


小結

整個系統的核心思想很簡單:

  1. R2 負責存檔案,便宜又快
  2. D1 負責存索引,搜尋靠它
  3. Workers 負責查詢邏輯
  4. Pages 負責呈現給使用者
  5. Python 腳本 負責定期餵資料進去

全部跑在 Cloudflare edge network 上,全球加速,零維護成本。

如果你也想建一個自己的圖庫,技術門檻其實沒有想像中高——只要會基本的 Python 和 JavaScript,這套架構完全可以在一個週末內跑起來。

實際成品可以參考:FreePicHub 自由圖匯


有技術規格或架構問題想諮詢?
歡迎來 ascentek.info 聯絡我們,無論是 Cloudflare 架構設計、Python 自動化流程,或是圖庫建置規劃,我們很樂意討論。


延伸閱讀

Ascentek數位知識庫