Skip to content

Bangun Pustaka Gambar Sendiri dengan Paket Gratis Cloudflare — Mulai dari $0

Pernahkah kamu berpikir untuk mengumpulkan semua gambar komersial gratis favoritmu dalam satu tempat, lalu tinggal cari — tanpa bayar sepeser pun?

Kami sudah melakukannya. Artikel ini menjelaskan seluruh arsitekturnya.


Kenapa Harus Bangun Sendiri?

Desainer, developer web, dan marketer selalu butuh gambar. Pixabay, Pexels, Unsplash semuanya bagus, tapi membuka masing-masing dan mencari secara terpisah itu membuang waktu.

Yang lebih menyebalkan: kamu sudah pakai sebuah gambar, tapi lain kali tidak ingat menemukannya di mana.

Kalau ada satu pintu masuk yang mengintegrasikan semua sumber, cari sekali hasilnya dari semua sumber — itu baru efisien.

Itulah ide di balik FreePicHub.


Seberapa Besar Kuota Gratis Cloudflare?

Banyak orang tidak tahu betapa murah hatinya Cloudflare. Berikut layanan yang digunakan proyek ini, semuanya dalam paket gratis:

LayananKegunaanKuota Gratis
R2 Object StorageSimpan file gambar10 GB storage / 10 juta GET/bulan
D1 DatabaseSimpan metadata gambar5 GB storage / 5 juta baca/hari
WorkersBackend API100 ribu request/hari
PagesWebsite frontendDeploy statis tanpa batas

Untuk pustaka gambar pribadi skala kecil hingga menengah, kuota ini lebih dari cukup — dan tidak perlu kartu kredit untuk mulai.


Gambaran Arsitektur

[Script Python Otomatis]

  Pemrosesan Gambar (kompresi WebP)

 ┌──────────────────┐
 │    Cloudflare    │
 │                  │
 │  R2 (file)       │
 │  D1 (database)   │
 │  Workers (API)   │
 │  Pages (UI)      │
 └──────────────────┘

    Browser / Pengguna

Empat layanan Cloudflare, masing-masing punya peran jelas:

  • R2: Menyimpan file gambar terkompresi (thumbnail / preview / original)
  • D1: Database SQLite — judul, dimensi, tag, path R2
  • Workers: Dua API — /api/search dan /api/featured
  • Pages: Frontend statis, HTML + CSS + JS murni, tanpa dependensi framework

Step 1: Buat R2 Bucket

Masuk Cloudflare Dashboard → R2 → Buat Bucket (misal my-image-library).

Lalu di Manage R2 API Tokens, buat User API Token:

  • Izin: "Object Read & Write"
  • Dibatasi untuk bucket kamu

Catat Access Key ID, Secret Access Key, dan Account ID — dibutuhkan script Python.

R2 menggunakan API kompatibel S3, jadi boto3 Python langsung bisa dipakai.


Step 2: Buat D1 Database

Jalankan di Wrangler CLI:

bash
npx wrangler d1 create my-image-db

Catat database_id yang dikembalikan. Lalu buat 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 (via Pages Functions)

Cara termudah adalah Cloudflare Pages Functions — taruh file JS di /functions/, Cloudflare otomatis deploy sebagai Worker.

Search 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: Pemrosesan Gambar & Ingesti Otomatis

Ini bagian paling menarik. Script Python memproses gambar, upload ke R2, dan menulis metadata ke D1.

Pemrosesan gambar — tiga versi per gambar, semua disimpan sebagai WebP:

python
from PIL import Image

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

    # Thumbnail 512px (galeri)
    thumb = img.copy()
    thumb.thumbnail((512, 512), Image.LANCZOS)
    thumb.save(output_dir / "thumb.webp", "WEBP", quality=82)

    # Preview 1024px (modal / lightbox)
    preview = img.copy()
    preview.thumbnail((1024, 1024), Image.LANCZOS)
    preview.save(output_dir / "preview.webp", "WEBP", quality=85)

    # Original dikonversi ke WebP terkompresi
    img.save(output_dir / "original.webp", "WEBP", quality=90)

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

WebP biasanya 25–35% lebih kecil dari JPEG — penghematan nyata di storage R2 jangka panjang.

Upload ke R2 (boto3 S3-compatible):

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)

Tulis ke D1 — aman dari duplikasi:

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)

Klausa WHERE NOT EXISTS memastikan script bisa dijalankan berulang kali tanpa menulis duplikat — penting untuk pipeline harian otomatis.


Step 5: Deploy Frontend

Tiga file statis — index.html, style.css, app.js — push ke Cloudflare Pages:

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

Layout masonry waterfall dengan CSS, tanpa library JS:

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

Kalkulasi Biaya

Asumsi 500 gambar/hari, pustaka 10.000 gambar (thumbnail ~50KB, preview ~200KB):

ItemPenggunaanBiaya
R2 Storage≈ 2,5 GB$0
D1 Reads (1.000 pengunjung/hari)5.000/hari$0
Workers Requests5.000/hari$0
Pages DeployTak terbatas$0
Total$0 / bulan

Kuota gratis Cloudflare hampir tidak habis untuk pustaka gambar pribadi atau komersial skala kecil.


Kesimpulan

Inti sistemnya sederhana:

  1. R2 menyimpan file — murah dan cepat
  2. D1 menyimpan indeks — kekuatan pencarian
  3. Workers menangani logika query
  4. Pages menyajikan UI ke pengguna
  5. Script Python mengisi data secara terjadwal

Semua berjalan di edge network Cloudflare — dipercepat secara global, tanpa biaya pemeliharaan.

Lihat hasil nyatanya: FreePicHub


Ada pertanyaan tentang spesifikasi teknis atau arsitektur?
Silakan hubungi kami di ascentek.info — baik itu desain arsitektur Cloudflare, pipeline otomasi Python, maupun perencanaan pustaka gambar, kami siap berdiskusi.


Bacaan Lanjutan

Basis Pengetahuan Digital Ascentek