
Cloudflare R2: บริการจัดเก็บข้อมูลแบบ Object Storage ที่ไม่มีค่า Egress
ประหยัดต้นทุนการจัดเก็บข้อมูลด้วยบริการที่เข้ากันได้กับ S3 แต่ไม่มีค่าใช้จ่ายในการดึงข้อมูลออก
1. ทำความเข้าใจ Cloudflare R2
R2 คืออะไร?
- นิยาม: R2 คือบริการ Object Storage ของ Cloudflare ที่เข้ากันได้กับ Amazon S3 API
- จุดเด่น: ไม่มีค่า Egress Bandwidth (ค่าดึงข้อมูลออก) ซึ่งต่างจาก AWS S3 และผู้ให้บริการรายอื่น
- ความสามารถหลัก:
- จัดเก็บไฟล์ได้ทุกประเภท (รูปภาพ, วิดีโอ, เอกสาร, แบ็คอัพ)
- เข้าถึงผ่าน API ที่เข้ากันได้กับ S3
- ทำงานร่วมกับ Cloudflare Workers และบริการอื่นๆ ได้อย่างไร้รอยต่อ
- Free Tier: 10GB storage และ 1 ล้าน Class A Operations ต่อเดือน
ทำไมต้องใช้ R2?
- ประหยัดต้นทุน: ไม่มีค่า Egress Bandwidth ซึ่งเป็นค่าใช้จ่ายหลักของบริการ Object Storage อื่นๆ
- ความเร็ว: กระจายข้อมูลผ่านเครือข่าย Cloudflare ทั่วโลก ทำให้เข้าถึงได้เร็ว
- ความเข้ากันได้: ใช้ API เดียวกับ S3 ทำให้ย้ายจาก AWS มาใช้ได้ง่าย
- ความปลอดภัย: ข้อมูลถูกเข้ารหัสทั้งขณะส่งและจัดเก็บ
- ความง่าย: จัดการผ่าน Dashboard หรือ API ได้สะดวก
2. เปรียบเทียบ R2 กับบริการ Object Storage อื่นๆ
R2 vs Amazon S3
คุณสมบัติ | Cloudflare R2 | Amazon S3 |
---|---|---|
ราคาเริ่มต้น | $0.015/GB/เดือน | $0.023/GB/เดือน |
Egress Bandwidth | ฟรี | $0.09/GB |
Free Tier | 10GB storage | 5GB storage |
API | S3-compatible | S3 |
การกระจายข้อมูล | ทั่วโลก | ตาม Region |
Storage Classes | 1 ประเภท | หลายประเภท |
Lifecycle Rules | มี | มี |
R2 vs Google Cloud Storage
คุณสมบัติ | Cloudflare R2 | Google Cloud Storage |
---|---|---|
ราคาเริ่มต้น | $0.015/GB/เดือน | $0.020/GB/เดือน |
Egress Bandwidth | ฟรี | $0.08-$0.12/GB |
Free Tier | 10GB storage | 5GB storage |
API | S3-compatible | GCS และ S3-compatible |
การกระจายข้อมูล | ทั่วโลก | ตาม Region |
Storage Classes | 1 ประเภท | หลายประเภท |
R2 vs Backblaze B2
คุณสมบัติ | Cloudflare R2 | Backblaze B2 |
---|---|---|
ราคาเริ่มต้น | $0.015/GB/เดือน | $0.005/GB/เดือน |
Egress Bandwidth | ฟรี | ฟรีผ่าน Cloudflare, $0.01/GB อื่นๆ |
Free Tier | 10GB storage | 10GB storage |
API | S3-compatible | B2 และ S3-compatible |
การกระจายข้อมูล | ทั่วโลก | US region |
การทำงานร่วมกับ Cloudflare | ไร้รอยต่อ | ต้องตั้งค่าเพิ่มเติม |
3. การเริ่มต้นใช้งาน R2
ขั้นตอนการสร้าง R2 Bucket
-
สร้างบัญชี Cloudflare
- ไปที่ cloudflare.com และสมัครบัญชีฟรี
- ยืนยันอีเมลของคุณ
-
เปิดใช้งาน R2
- ไปที่ Cloudflare Dashboard
- เลือก “R2” จากเมนูด้านซ้าย
- คลิก “Create bucket”
-
ตั้งค่า Bucket
- ตั้งชื่อ bucket (ต้องเป็นชื่อที่ไม่ซ้ำกันทั่วโลก)
- เลือก Region (หรือใช้ค่าเริ่มต้น)
- ตั้งค่าการเข้าถึง (Public หรือ Private)
-
ตั้งค่า API Token
- ไปที่ “R2” > “Manage R2 API Tokens”
- สร้าง Token ใหม่สำหรับการเข้าถึง R2 API
- บันทึก Access Key และ Secret Key ไว้ให้ดี
การอัปโหลดไฟล์ผ่าน Dashboard
-
เข้าสู่ Bucket
- ไปที่ R2 Dashboard
- เลือก bucket ที่ต้องการ
-
อัปโหลดไฟล์
- คลิก “Upload” หรือลากไฟล์มาวาง
- เลือกไฟล์ที่ต้องการอัปโหลด
- ตั้งค่า metadata หรือ headers (ถ้าต้องการ)
-
จัดการไฟล์
- ดูรายการไฟล์ทั้งหมดใน bucket
- ดาวน์โหลด, ลบ, หรือแก้ไขข้อมูลของไฟล์
- คัดลอก URL สำหรับเข้าถึงไฟล์
การใช้งาน R2 ผ่าน S3 CLI
-
ติดตั้ง AWS CLI
pip install awscli
-
ตั้งค่า Profile สำหรับ R2
aws configure --profile r2
- AWS Access Key ID: [R2 Access Key]
- AWS Secret Access Key: [R2 Secret Key]
- Default region name: auto
- Default output format: json
-
ใช้งานคำสั่งพื้นฐาน
# ดูรายการ buckets aws s3 ls --endpoint-url https://[ACCOUNT_ID].r2.cloudflarestorage.com --profile r2 # อัปโหลดไฟล์ aws s3 cp myfile.jpg s3://my-bucket/ --endpoint-url https://[ACCOUNT_ID].r2.cloudflarestorage.com --profile r2 # ดาวน์โหลดไฟล์ aws s3 cp s3://my-bucket/myfile.jpg ./myfile.jpg --endpoint-url https://[ACCOUNT_ID].r2.cloudflarestorage.com --profile r2 # ลบไฟล์ aws s3 rm s3://my-bucket/myfile.jpg --endpoint-url https://[ACCOUNT_ID].r2.cloudflarestorage.com --profile r2
4. การใช้งาน R2 กับภาษาโปรแกรมต่างๆ
JavaScript/Node.js (AWS SDK)
// ติดตั้ง: npm install @aws-sdk/client-s3
const { S3Client, PutObjectCommand, GetObjectCommand } = require("@aws-sdk/client-s3");
// ตั้งค่าการเชื่อมต่อ
const s3 = new S3Client({
region: "auto",
endpoint: `https://[ACCOUNT_ID].r2.cloudflarestorage.com`,
credentials: {
accessKeyId: "R2_ACCESS_KEY",
secretAccessKey: "R2_SECRET_KEY"
}
});
// อัปโหลดไฟล์
async function uploadFile(bucketName, key, body) {
const command = new PutObjectCommand({
Bucket: bucketName,
Key: key,
Body: body
});
return s3.send(command);
}
// ดาวน์โหลดไฟล์
async function downloadFile(bucketName, key) {
const command = new GetObjectCommand({
Bucket: bucketName,
Key: key
});
const response = await s3.send(command);
return response.Body;
}
Python (boto3)
# ติดตั้ง: pip install boto3
import boto3
# ตั้งค่าการเชื่อมต่อ
s3 = boto3.client('s3',
endpoint_url = 'https://[ACCOUNT_ID].r2.cloudflarestorage.com',
aws_access_key_id = 'R2_ACCESS_KEY',
aws_secret_access_key = 'R2_SECRET_KEY'
)
# อัปโหลดไฟล์
def upload_file(bucket_name, key, file_path):
s3.upload_file(file_path, bucket_name, key)
# ดาวน์โหลดไฟล์
def download_file(bucket_name, key, file_path):
s3.download_file(bucket_name, key, file_path)
# ดูรายการไฟล์
def list_files(bucket_name):
response = s3.list_objects_v2(Bucket=bucket_name)
if 'Contents' in response:
for obj in response['Contents']:
print(obj['Key'])
PHP (AWS SDK)
// ติดตั้ง: composer require aws/aws-sdk-php
require 'vendor/autoload.php';
use Aws\S3\S3Client;
// ตั้งค่าการเชื่อมต่อ
$s3 = new S3Client([
'endpoint' => 'https://[ACCOUNT_ID].r2.cloudflarestorage.com',
'region' => 'auto',
'version' => 'latest',
'credentials' => [
'key' => 'R2_ACCESS_KEY',
'secret' => 'R2_SECRET_KEY',
]
]);
// อัปโหลดไฟล์
function uploadFile($bucketName, $key, $filePath) {
global $s3;
$result = $s3->putObject([
'Bucket' => $bucketName,
'Key' => $key,
'SourceFile' => $filePath
]);
return $result;
}
// ดาวน์โหลดไฟล์
function downloadFile($bucketName, $key, $savePath) {
global $s3;
$result = $s3->getObject([
'Bucket' => $bucketName,
'Key' => $key,
'SaveAs' => $savePath
]);
return $result;
}
5. การใช้งาน R2 กับ Cloudflare Workers
การเข้าถึง R2 จาก Workers
// wrangler.toml
// [[r2_buckets]]
// binding = "MY_BUCKET"
// bucket_name = "my-bucket"
export default {
async fetch(request, env, ctx) {
// ดึงข้อมูลจาก URL
const url = new URL(request.url);
const key = url.pathname.slice(1); // ตัด / ออกจากด้านหน้า
if (request.method === "GET") {
// ดาวน์โหลดไฟล์
try {
const object = await env.MY_BUCKET.get(key);
if (object === null) {
return new Response("Object Not Found", { status: 404 });
}
const headers = new Headers();
object.writeHttpMetadata(headers);
headers.set("etag", object.httpEtag);
return new Response(object.body, {
headers
});
} catch (e) {
return new Response("Error: " + e.message, { status: 500 });
}
} else if (request.method === "PUT") {
// อัปโหลดไฟล์
try {
const object = await env.MY_BUCKET.put(key, request.body, {
httpMetadata: request.headers
});
return new Response("Object Uploaded Successfully", { status: 200 });
} catch (e) {
return new Response("Error: " + e.message, { status: 500 });
}
} else if (request.method === "DELETE") {
// ลบไฟล์
try {
await env.MY_BUCKET.delete(key);
return new Response("Object Deleted Successfully", { status: 200 });
} catch (e) {
return new Response("Error: " + e.message, { status: 500 });
}
}
return new Response("Method Not Allowed", { status: 405 });
}
};
การสร้าง Image Resizing Service
// wrangler.toml
// [[r2_buckets]]
// binding = "IMAGES_BUCKET"
// bucket_name = "my-images"
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
const path = url.pathname;
// รูปแบบ URL: /resize/{width}x{height}/{image-key}
const match = path.match(/^\/resize\/(\d+)x(\d+)\/(.+)$/);
if (!match) {
return new Response("Invalid URL format", { status: 400 });
}
const width = parseInt(match[1]);
const height = parseInt(match[2]);
const imageKey = match[3];
// ดึงรูปภาพจาก R2
const object = await env.IMAGES_BUCKET.get(imageKey);
if (object === null) {
return new Response("Image Not Found", { status: 404 });
}
// ปรับขนาดรูปภาพด้วย Cloudflare Image Resizing
const imageURL = new URL(request.url);
imageURL.pathname = imageKey;
const resizedImage = await fetch(imageURL.toString(), {
cf: {
image: {
width: width,
height: height,
fit: "cover"
}
}
});
// ส่งรูปภาพที่ปรับขนาดแล้วกลับไป
return new Response(resizedImage.body, {
headers: {
"Content-Type": object.httpMetadata.contentType,
"Cache-Control": "public, max-age=31536000"
}
});
}
};
การสร้าง Public URL Generator
// wrangler.toml
// [[r2_buckets]]
// binding = "FILES_BUCKET"
// bucket_name = "my-files"
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
if (url.pathname === "/generate-url" && request.method === "POST") {
try {
const { key, expirationSeconds = 3600 } = await request.json();
if (!key) {
return new Response(JSON.stringify({ error: "Key is required" }), {
status: 400,
headers: { "Content-Type": "application/json" }
});
}
// ตรวจสอบว่าไฟล์มีอยู่จริง
const object = await env.FILES_BUCKET.head(key);
if (object === null) {
return new Response(JSON.stringify({ error: "File not found" }), {
status: 404,
headers: { "Content-Type": "application/json" }
});
}
// สร้าง signed URL
const signedUrl = await env.FILES_BUCKET.createSignedUrl(key, expirationSeconds);
return new Response(JSON.stringify({ url: signedUrl }), {
headers: { "Content-Type": "application/json" }
});
} catch (e) {
return new Response(JSON.stringify({ error: e.message }), {
status: 500,
headers: { "Content-Type": "application/json" }
});
}
}
return new Response("Not Found", { status: 404 });
}
};
6. การตั้งค่า Public Access และ Custom Domain
การตั้งค่า Public Access
-
เปิดใช้งาน Public Access
- ไปที่ R2 Dashboard > เลือก bucket
- ไปที่ “Settings” > “Public Access”
- เปิดใช้งาน “Public Access”
-
การตั้งค่า CORS (Cross-Origin Resource Sharing)
[ { "AllowedOrigins": ["https://example.com"], "AllowedMethods": ["GET"], "AllowedHeaders": ["*"], "MaxAgeSeconds": 3600 } ]
-
การเข้าถึงไฟล์แบบ Public
- URL รูปแบบ:
https://pub-[HASH].r2.dev/[BUCKET_NAME]/[FILE_PATH]
- ตัวอย่าง:
https://pub-abc123.r2.dev/my-bucket/images/photo.jpg
- URL รูปแบบ:
การตั้งค่า Custom Domain
-
เพิ่ม Custom Domain ใน Cloudflare
- ต้องมีโดเมนที่ใช้ Cloudflare DNS
- ไปที่ R2 Dashboard > “Custom Domains”
- คลิก “Add Custom Domain”
- ใส่โดเมนที่ต้องการ (เช่น
assets.example.com
)
-
ตั้งค่า DNS Record
- Cloudflare จะสร้าง CNAME record ให้อัตโนมัติ
- ตรวจสอบว่า Proxy Status เป็น “Proxied”
-
การเข้าถึงไฟล์ผ่าน Custom Domain
- URL รูปแบบ:
https://[CUSTOM_DOMAIN]/[FILE_PATH]
- ตัวอย่าง:
https://assets.example.com/images/photo.jpg
- URL รูปแบบ:
-
การตั้งค่า Cache
- ไปที่ Cloudflare Dashboard > Rules > Cache Rules
- สร้าง Cache Rule สำหรับ Custom Domain
- ตั้งค่า Edge TTL และ Browser TTL ตามต้องการ
7. การใช้งาน R2 กับเว็บไซต์และแอปพลิเคชัน
การใช้ R2 เก็บรูปภาพสำหรับเว็บไซต์
-
การอัปโหลดรูปภาพ
// ตัวอย่างการอัปโหลดรูปภาพจาก form const form = document.getElementById('upload-form'); form.addEventListener('submit', async (e) => { e.preventDefault(); const fileInput = document.getElementById('image-file'); const file = fileInput.files[0]; if (!file) { alert('Please select a file'); return; } // ส่งไฟล์ไปยัง API ที่เชื่อมต่อกับ R2 const formData = new FormData(); formData.append('image', file); const response = await fetch('/api/upload', { method: 'POST', body: formData }); const result = await response.json(); if (result.success) { document.getElementById('image-url').textContent = result.url; document.getElementById('preview').src = result.url; } else { alert('Upload failed: ' + result.error); } });
-
การแสดงรูปภาพบนเว็บไซต์
<!-- การใช้ Public URL --> <img src="https://pub-abc123.r2.dev/my-bucket/images/photo.jpg" alt="My Photo"> <!-- การใช้ Custom Domain --> <img src="https://assets.example.com/images/photo.jpg" alt="My Photo"> <!-- การใช้ Image Resizing --> <img src="https://assets.example.com/resize/400x300/images/photo.jpg" alt="My Photo">
การใช้ R2 เก็บไฟล์สำหรับแอปพลิเคชัน
-
การอัปโหลดไฟล์จากแอปพลิเคชัน
// React/Next.js ตัวอย่าง import { useState } from 'react'; function FileUploader() { const [file, setFile] = useState(null); const [uploading, setUploading] = useState(false); const [downloadUrl, setDownloadUrl] = useState(''); const handleFileChange = (e) => { setFile(e.target.files[0]); }; const handleUpload = async () => { if (!file) return; setUploading(true); const formData = new FormData(); formData.append('file', file); try { const response = await fetch('/api/upload-file', { method: 'POST', body: formData }); const data = await response.json(); if (data.success) { setDownloadUrl(data.url); alert('File uploaded successfully!'); } else { alert('Upload failed: ' + data.error); } } catch (error) { alert('Error: ' + error.message); } finally { setUploading(false); } }; return ( <div> <input type="file" onChange={handleFileChange} /> <button onClick={handleUpload} disabled={!file || uploading}> {uploading ? 'Uploading...' : 'Upload'} </button> {downloadUrl && ( <div> <p>File uploaded successfully!</p> <a href={downloadUrl} target="_blank" rel="noopener noreferrer"> Download File </a> </div> )} </div> ); }
-
การสร้าง API สำหรับจัดการไฟล์
// Next.js API Route import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'; import { v4 as uuidv4 } from 'uuid'; import formidable from 'formidable'; import fs from 'fs'; export const config = { api: { bodyParser: false, }, }; const s3 = new S3Client({ region: 'auto', endpoint: `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`, credentials: { accessKeyId: process.env.R2_ACCESS_KEY, secretAccessKey: process.env.R2_SECRET_KEY, }, }); export default async function handler(req, res) { if (req.method !== 'POST') { return res.status(405).json({ error: 'Method not allowed' }); } try { const form = new formidable.IncomingForm(); form.parse(req, async (err, fields, files) => { if (err) { return res.status(500).json({ error: 'Error parsing form' }); } const file = files.file; if (!file) { return res.status(400).json({ error: 'No file uploaded' }); } const fileContent = fs.readFileSync(file.filepath); const fileKey = `uploads/${uuidv4()}-${file.originalFilename}`; const command = new PutObjectCommand({ Bucket: process.env.R2_BUCKET_NAME, Key: fileKey, Body: fileContent, ContentType: file.mimetype, }); await s3.send(command); const fileUrl = `https://${process.env.R2_CUSTOM_DOMAIN}/${fileKey}`; res.status(200).json({ success: true, url: fileUrl, }); }); } catch (error) { console.error('Upload error:', error); res.status(500).json({ error: 'Upload failed' }); } }
8. การใช้ R2 สำหรับ Backup และ Archive
การสร้างระบบ Backup อัตโนมัติ
-
การสร้าง Backup Script ด้วย Python
import boto3 import os import datetime import tarfile # ตั้งค่าการเชื่อมต่อ R2 s3 = boto3.client('s3', endpoint_url = 'https://[ACCOUNT_ID].r2.cloudflarestorage.com', aws_access_key_id = 'R2_ACCESS_KEY', aws_secret_access_key = 'R2_SECRET_KEY' ) # ตั้งค่าพารามิเตอร์ BUCKET_NAME = 'my-backups' BACKUP_DIR = '/path/to/backup' def create_backup(): # สร้างชื่อไฟล์ด้วยวันที่และเวลาปัจจุบัน timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S') backup_file = f'backup_{timestamp}.tar.gz' # สร้างไฟล์ tar.gz with tarfile.open(backup_file, 'w:gz') as tar: tar.add(BACKUP_DIR, arcname=os.path.basename(BACKUP_DIR)) # อัปโหลดไฟล์ไปยัง R2 print(f'Uploading {backup_file} to R2...') s3.upload_file(backup_file, BUCKET_NAME, backup_file) # ลบไฟล์ชั่วคราว os.remove(backup_file) print(f'Backup completed: {backup_file}') # ลบไฟล์ backup เก่า (เก็บไว้แค่ 7 วันล่าสุด) delete_old_backups(7) def delete_old_backups(days_to_keep): # ดึงรายการไฟล์ทั้งหมด response = s3.list_objects_v2(Bucket=BUCKET_NAME) if 'Contents' not in response: return # คำนวณวันที่ที่เก่ากว่า X วัน cutoff_date = datetime.datetime.now() - datetime.timedelta(days=days_to_keep) for obj in response['Contents']: # ตรวจสอบว่าเป็นไฟล์ backup หรือไม่ if obj['Key'].startswith('backup_'): # ดึงวันที่จากชื่อไฟล์ (format: backup_YYYYMMDD_HHMMSS.tar.gz) try: date_str = obj['Key'].split('_')[1].split('.')[0] file_date = datetime.datetime.strptime(date_str, '%Y%m%d_%H%M%S') # ลบไฟล์ที่เก่ากว่า X วัน if file_date < cutoff_date: print(f"Deleting old backup: {obj['Key']}") s3.delete_object(Bucket=BUCKET_NAME, Key=obj['Key']) except: pass if __name__ == '__main__': create_backup()
-
การตั้งค่า Cron Job
# เปิด crontab editor crontab -e # เพิ่มบรรทัดนี้เพื่อรัน script ทุกวันเวลา 02:00 น. 0 2 * * * /usr/bin/python3 /path/to/backup_script.py >> /path/to/backup.log 2>&1
การกู้คืนข้อมูลจาก Backup
import boto3
import tarfile
import os
# ตั้งค่าการเชื่อมต่อ R2
s3 = boto3.client('s3',
endpoint_url = 'https://[ACCOUNT_ID].r2.cloudflarestorage.com',
aws_access_key_id = 'R2_ACCESS_KEY',
aws_secret_access_key = 'R2_SECRET_KEY'
)
# ตั้งค่าพารามิเตอร์
BUCKET_NAME = 'my-backups'
RESTORE_DIR = '/path/to/restore'
def list_backups():
# ดึงรายการไฟล์ backup ทั้งหมด
response = s3.list_objects_v2(Bucket=BUCKET_NAME)
if 'Contents' not in response:
print("No backups found")
return []
# กรองเฉพาะไฟล์ backup
backups = [obj['Key'] for obj in response['Contents'] if obj['Key'].startswith('backup_')]
backups.sort(reverse=True) # เรียงจากใหม่ไปเก่า
# แสดงรายการ
for i, backup in enumerate(backups):
print(f"{i+1}. {backup}")
return backups
def restore_backup(backup_file):
# ดาวน์โหลดไฟล์ backup
print(f"Downloading {backup_file}...")
s3.download_file(BUCKET_NAME, backup_file, backup_file)
# สร้างโฟลเดอร์สำหรับกู้คืนข้อมูล (ถ้ายังไม่มี)
os.makedirs(RESTORE_DIR, exist_ok=True)
# แตกไฟล์ tar.gz
print(f"Extracting {backup_file} to {RESTORE_DIR}...")
with tarfile.open(backup_file, 'r:gz') as tar:
tar.extractall(path=RESTORE_DIR)
# ลบไฟล์ชั่วคราว
os.remove(backup_file)
print(f"Restore completed to {RESTORE_DIR}")
if __name__ == '__main__':
backups = list_backups()
if not backups:
exit(0)
choice = input("\nEnter the number of the backup to restore (or 'q' to quit): ")
if choice.lower() == 'q':
exit(0)
try:
index = int(choice) - 1
if 0 <= index < len(backups):
restore_backup(backups[index])
else:
print("Invalid selection")
except ValueError:
print("Invalid input")
9. การจัดการต้นทุนและการติดตามการใช้งาน
การติดตามการใช้งานและค่าใช้จ่าย
-
การดูสถิติการใช้งาน
- ไปที่ Cloudflare Dashboard > R2
- ดูแท็บ “Usage” เพื่อดูข้อมูล:
- Storage Used
- Class A Operations (PUT, POST, LIST)
- Class B Operations (GET)
- Egress Bandwidth
-
การตั้งค่าการแจ้งเตือน
- ไปที่ Cloudflare Dashboard > R2 > “Usage Alerts”
- ตั้งค่าการแจ้งเตือนเมื่อ:
- Storage เกินกำหนด
- Operations เกินกำหนด
- เลือกช่องทางการแจ้งเตือน (Email, Webhook)
เทคนิคการประหยัดต้นทุน
-
การใช้ Lifecycle Rules
- ตั้งค่า Lifecycle Rules เพื่อลบไฟล์ที่ไม่ได้ใช้งานอัตโนมัติ
- ไปที่ R2 Dashboard > เลือก bucket > “Lifecycle Rules”
- ตัวอย่าง: ลบไฟล์ชั่วคราวหลังจาก 7 วัน
-
การบีบอัดไฟล์ก่อนอัปโหลด
// ตัวอย่างการบีบอัดรูปภาพก่อนอัปโหลด async function compressAndUploadImage(file) { // ใช้ browser-image-compression library const imageFile = await imageCompression(file, { maxSizeMB: 1, maxWidthOrHeight: 1920 }); // อัปโหลดไฟล์ที่บีบอัดแล้ว const formData = new FormData(); formData.append('image', imageFile); const response = await fetch('/api/upload', { method: 'POST', body: formData }); return response.json(); }
-
การใช้ Cache อย่างมีประสิทธิภาพ
- ตั้งค่า Cache-Control headers ให้เหมาะสม
- ตัวอย่าง:
Cache-Control: public, max-age=31536000
สำหรับไฟล์สถิต - ใช้ Cloudflare Cache Rules เพื่อเพิ่มประสิทธิภาพ
10. กรณีศึกษา: การใช้งานจริงของ Startup
กรณีศึกษา 1: เว็บไซต์ Content
- ความท้าทาย: Startup ด้านคอนเทนต์ต้องการพื้นที่เก็บรูปภาพและวิดีโอที่ประหยัดต้นทุน
- การใช้ R2:
- เก็บรูปภาพและวิดีโอทั้งหมดใน R2
- ใช้ Cloudflare Workers เพื่อปรับขนาดรูปภาพตามอุปกรณ์
- ตั้งค่า Custom Domain สำหรับเข้าถึงไฟล์มีเดีย
- ผลลัพธ์:
- ประหยัดค่า bandwidth ได้มากกว่า 90% เมื่อเทียบกับ AWS S3
- เว็บไซต์โหลดเร็วขึ้นเนื่องจากไฟล์มีเดียอยู่บนเครือข่าย Cloudflare
- ลดความซับซ้อนในการจัดการไฟล์มีเดีย
กรณีศึกษา 2: แอปพลิเคชันแชร์ไฟล์
- ความท้าทาย: Startup ต้องการสร้างแพลตฟอร์มแชร์ไฟล์ที่มีต้นทุนต่ำ
- การใช้ R2:
- เก็บไฟล์ทั้งหมดใน R2
- ใช้ Workers เพื่อสร้าง Signed URLs ที่หมดอายุ
- สร้างระบบจัดการสิทธิ์การเข้าถึงไฟล์
- ผลลัพธ์:
- ประหยัดค่าใช้จ่ายได้มากกว่า 80% เมื่อเทียบกับผู้ให้บริการรายอื่น
- ผู้ใช้สามารถดาวน์โหลดไฟล์ได้เร็วขึ้นทั่วโลก
- สามารถขยายธุรกิจได้โดยไม่กังวลเรื่องค่า bandwidth
กรณีศึกษา 3: ระบบ Backup อัตโนมัติ
- ความท้าทาย: Startup ด้าน SaaS ต้องการระบบ backup ที่เชื่อถือได้และประหยัด
- การใช้ R2:
- สร้างระบบ backup อัตโนมัติที่เก็บข้อมูลใน R2
- ตั้งค่า lifecycle rules เพื่อลบ backup เก่า
- สร้างระบบกู้คืนข้อมูลที่ใช้งานง่าย
- ผลลัพธ์:
- ลดค่าใช้จ่ายในการเก็บ backup ลง 70%
- เพิ่มความเร็วในการกู้คืนข้อมูล
- ระบบทำงานอัตโนมัติ ลดภาระของทีม DevOps
11. ข้อจำกัดและการอัพเกรด
ข้อจำกัดของ R2
-
Free Tier:
- 10GB storage
- 1 ล้าน Class A Operations ต่อเดือน
- 10 ล้าน Class B Operations ต่อเดือน
- ไม่จำกัด Egress Bandwidth
-
ข้อจำกัดทางเทคนิค:
- ขนาดไฟล์สูงสุด: 5TB
- จำนวน buckets สูงสุด: 1,000 ต่อบัญชี
- ไม่รองรับ Server-Side Encryption ด้วย Customer-Provided Keys
- ไม่รองรับ Multi-Region Replication แบบอัตโนมัติ
เมื่อไรควรพิจารณาแพ็กเกจ Paid
-
ปริมาณการใช้งาน:
- ใช้พื้นที่เกิน 10GB
- มี Class A Operations เกิน 1 ล้านต่อเดือน
- มี Class B Operations เกิน 10 ล้านต่อเดือน
-
ความต้องการพิเศษ:
- ต้องการ Support แบบ Priority
- ต้องการการรับประกันด้าน SLA
- ต้องการฟีเจอร์ Enterprise เพิ่มเติม
-
การเติบโตของธุรกิจ:
- มีแผนขยายการใช้งานในอนาคต
- ต้องการความยืดหยุ่นในการใช้งาน
- ต้องการการรองรับจากทีม Cloudflare
ราคาและแพ็กเกจ
-
Free Tier:
- 10GB storage
- 1 ล้าน Class A Operations ต่อเดือน
- 10 ล้าน Class B Operations ต่อเดือน
- ไม่จำกัด Egress Bandwidth
-
Paid Tier:
- Storage: $0.015/GB/เดือน (หลังจาก 10GB แรก)
- Class A Operations: $4.50/ล้าน (หลังจาก 1 ล้านแรก)
- Class B Operations: $0.36/10 ล้าน (หลังจาก 10 ล้านแรก)
- Egress Bandwidth: ฟรีทั้งหมด
12. สรุป: ทำไม Cloudflare R2 ถึงเหมาะกับ Startup
-
ประหยัดต้นทุน
ไม่มีค่า Egress Bandwidth ซึ่งเป็นค่าใช้จ่ายหลักของบริการ Object Storage อื่นๆ -
ความเข้ากันได้
ใช้ API เดียวกับ S3 ทำให้ย้ายจาก AWS มาใช้ได้ง่าย ไม่ต้องเขียนโค้ดใหม่ -
ประสิทธิภาพสูง
กระจายข้อมูลผ่านเครือข่าย Cloudflare ทั่วโลก ทำให้เข้าถึงได้เร็ว -
ความปลอดภัย
ข้อมูลถูกเข้ารหัสทั้งขณะส่งและจัดเก็บ พร้อมระบบป้องกันการโจมตี -
ทำงานร่วมกับบริการอื่น
ทำงานร่วมกับ Cloudflare Workers, Pages และบริการอื่นๆ ได้อย่างไร้รอยต่อ
“Cloudflare R2 เป็นทางเลือกที่ชาญฉลาดสำหรับ Startup ที่ต้องการลดต้นทุนด้าน Object Storage โดยไม่ต้องกังวลเรื่องค่า bandwidth ที่ไม่คาดคิด ทำให้สามารถเติบโตได้อย่างยั่งยืน”
13. แหล่งเรียนรู้เพิ่มเติม
เอกสารและบทความ:
คอมมูนิตี้:
คอร์สและวิดีโอ:
เคล็ดลับ: Cloudflare มีการอัพเดทฟีเจอร์ใหม่ๆ สำหรับ R2 อยู่เสมอ ติดตาม Cloudflare Blog และ Twitter เพื่อไม่พลาดฟีเจอร์ล่าสุดที่อาจช่วยให้การใช้งาน Object Storage ของคุณดียิ่งขึ้น