อัปเดตบทความใหม่ทุกสัปดาห์

DigitalOcean Spaces

S3-Compatible Object Storage พร้อม CDN ในตัว

คู่มือใช้งาน DigitalOcean Spaces สำหรับจัดเก็บไฟล์ พร้อมตัวอย่างโค้ด Node.js และ Python

Spaces
Node.js
Python
CDN

DigitalOcean Spaces คืออะไร?

DigitalOcean Spaces คือบริการ Object Storage ที่เข้ากันได้กับ Amazon S3 API ทำให้คุณสามารถใช้เครื่องมือและไลบรารี ที่มีอยู่แล้วได้ทันที โดยมีคุณสมบัติหลักดังนี้:

  • S3-Compatible: ใช้ AWS SDK ที่มีอยู่ได้ทันที ไม่ต้องเรียนรู้ใหม่
  • CDN ในตัว: เร่งความเร็วการโหลดไฟล์ทั่วโลกอัตโนมัติ
  • Scalable: จัดเก็บข้อมูลได้ไม่จำกัด ไม่ต้องกังวลเรื่อง capacity
  • ราคาประหยัด: เริ่มต้น $5/เดือน สำหรับ 250GB storage
  • High Availability: ข้อมูลถูก replicate อัตโนมัติ
  • SSL/TLS: เข้ารหัสการเชื่อมต่ออัตโนมัติ

Use Cases ยอดนิยม

Static Assets

เก็บรูปภาพ, CSS, JavaScript สำหรับเว็บไซต์

Media Files

เก็บวิดีโอ, ไฟล์เสียง, เอกสาร PDF

Backup & Archive

สำรองข้อมูล database, log files

Mobile Apps

เก็บไฟล์ที่ผู้ใช้อัปโหลดจากแอป

User Uploads

Profile pictures, avatars, documents

CI/CD Artifacts

เก็บ build artifacts, deployment files

Architecture Overview

DigitalOcean Spaces Architecture Node.js App AWS SDK v3 @aws-sdk/client-s3 Python App Boto3 SDK S3 Client DigitalOcean Spaces https://nyc3.digitaloceanspaces.com Built-in CDN Edge Locations Worldwide my-bucket (Bucket) 📁 images/ 📁 videos/ 📁 backups/ 📁 docs/ 📄 profile.jpg (public-read) 🔒 database.sql.gz (private) 📄 tutorial.mp4 (public-read)

Architecture แสดงการเชื่อมต่อจาก Application ไปยัง DigitalOcean Spaces ผ่าน S3 API

สิ่งที่ต้องเตรียม

DigitalOcean Account

  • • สร้าง account ที่ digitalocean.com
  • • เติมเงินขั้นต่ำ $5
  • • ใช้งานได้ทันทีหลัง verify

API Keys (Access Key + Secret)

  • • ไปที่ API → Spaces Keys
  • • กด Generate New Key
  • • เก็บ Key และ Secret ไว้ให้ปลอดภัย

Space (Bucket)

  • • ไปที่ Spaces → Create Space
  • • เลือก Region (แนะนำ Singapore สำหรับไทย)
  • • ตั้งชื่อ bucket ที่ unique

Development Environment

  • • Node.js 18+ หรือ Python 3.8+
  • • npm หรือ pip
  • • Code editor (VS Code แนะนำ)
1

สร้าง Space และ API Keys

สร้าง Space (Bucket)

  1. 1 เข้าสู่ DigitalOcean Control Panel
  2. 2 ไปที่เมนู SpacesCreate Space
  3. 3 เลือก Region: Singapore (sgp1) - ใกล้ไทยที่สุด
  4. 4 ตั้งชื่อ Space: เช่น my-app-assets
  5. 5 เลือก Restrict File Listing เพื่อความปลอดภัย
  6. 6 กด Create Space

สร้าง API Keys

  1. 1 ไปที่ APISpaces Keys
  2. 2 กด Generate New Key
  3. 3 ตั้งชื่อ Key: เช่น my-app-key
  4. 4 คัดลอก Access Key และ Secret Key เก็บไว้

สำคัญ: Secret Key จะแสดงเฉพาะครั้งเดียว หากลืมต้องสร้าง Key ใหม่

ตั้งค่า Environment Variables

สร้างไฟล์ .env เพื่อเก็บ credentials:

.env
# DigitalOcean Spaces Credentials
DO_SPACES_KEY=your_access_key_here
DO_SPACES_SECRET=your_secret_key_here
DO_SPACES_BUCKET=my-app-assets
DO_SPACES_REGION=sgp1
DO_SPACES_ENDPOINT=https://sgp1.digitaloceanspaces.com
2

Node.js Integration

ติดตั้ง Dependencies

Terminal
# สร้างโปรเจคใหม่
mkdir digitalocean-spaces-demo
cd digitalocean-spaces-demo
npm init -y

# ติดตั้ง AWS SDK v3 (S3 client)
npm install @aws-sdk/client-s3

# ติดตั้ง dotenv สำหรับ environment variables
npm install dotenv

# ติดตั้ง multer สำหรับ file upload (optional)
npm install multer

สร้าง S3 Client

spaces-client.js
import { S3Client } from '@aws-sdk/client-s3';
import dotenv from 'dotenv';

dotenv.config();

const s3Client = new S3Client({
    forcePathStyle: false, // ใช้ subdomain/virtual calling
    region: 'us-east-1', // AWS region (ต้องใช้ us-east-1)
    endpoint: process.env.DO_SPACES_ENDPOINT,
    credentials: {
        accessKeyId: process.env.DO_SPACES_KEY,
        secretAccessKey: process.env.DO_SPACES_SECRET
    }
});

export default s3Client;

หมายเหตุ: ต้องใช้ region: 'us-east-1' เพราะเป็น requirement ของ AWS SDK แต่ endpoint จะชี้ไป DigitalOcean จริง

อัปโหลดไฟล์

upload.js
import { PutObjectCommand } from '@aws-sdk/client-s3';
import fs from 'fs';
import s3Client from './spaces-client.js';

async function uploadFile(localPath, remoteKey) {
    const fileContent = fs.readFileSync(localPath);

    const params = {
        Bucket: process.env.DO_SPACES_BUCKET,
        Key: remoteKey, // เช่น 'images/profile.jpg'
        Body: fileContent,
        ACL: 'public-read', // หรือ 'private'
        ContentType: 'image/jpeg' // MIME type
    };

    try {
        const data = await s3Client.send(new PutObjectCommand(params));
        console.log('✅ Upload successful!');
        console.log('File URL:', `https://${process.env.DO_SPACES_BUCKET}.sgp1.digitaloceanspaces.com/${remoteKey}`);
        return data;
    } catch (err) {
        console.error('❌ Upload error:', err);
        throw err;
    }
}

// ตัวอย่างการใช้งาน
uploadFile('./local-image.jpg', 'uploads/profile-2024.jpg');

ดาวน์โหลดไฟล์

download.js
import { GetObjectCommand } from '@aws-sdk/client-s3';
import fs from 'fs';
import s3Client from './spaces-client.js';

async function downloadFile(remoteKey, localPath) {
    const params = {
        Bucket: process.env.DO_SPACES_BUCKET,
        Key: remoteKey
    };

    try {
        const data = await s3Client.send(new GetObjectCommand(params));

        // แปลง stream เป็น buffer
        const chunks = [];
        for await (const chunk of data.Body) {
            chunks.push(chunk);
        }
        const buffer = Buffer.concat(chunks);

        // บันทึกไฟล์
        fs.writeFileSync(localPath, buffer);
        console.log(`✅ Downloaded: ${remoteKey} → ${localPath}`);
    } catch (err) {
        console.error('❌ Download error:', err);
        throw err;
    }
}

// ตัวอย่างการใช้งาน
downloadFile('uploads/profile-2024.jpg', './downloaded-image.jpg');

แสดงรายการไฟล์

list-files.js
import { ListObjectsV2Command } from '@aws-sdk/client-s3';
import s3Client from './spaces-client.js';

async function listFiles(prefix = '') {
    const params = {
        Bucket: process.env.DO_SPACES_BUCKET,
        Prefix: prefix, // เช่น 'images/' เพื่อกรองเฉพาะโฟลเดอร์
        MaxKeys: 100
    };

    try {
        const data = await s3Client.send(new ListObjectsV2Command(params));

        console.log(`📁 Files in bucket (${data.KeyCount} items):`);

        data.Contents?.forEach(item => {
            const size = (item.Size / 1024).toFixed(2);
            console.log(`  - ${item.Key} (${size} KB)`);
        });

        return data.Contents;
    } catch (err) {
        console.error('❌ List error:', err);
        throw err;
    }
}

// ตัวอย่างการใช้งาน
listFiles('uploads/'); // แสดงไฟล์ในโฟลเดอร์ uploads

ลบไฟล์

delete.js
import { DeleteObjectCommand } from '@aws-sdk/client-s3';
import s3Client from './spaces-client.js';

async function deleteFile(remoteKey) {
    const params = {
        Bucket: process.env.DO_SPACES_BUCKET,
        Key: remoteKey
    };

    try {
        await s3Client.send(new DeleteObjectCommand(params));
        console.log(`🗑️ Deleted: ${remoteKey}`);
    } catch (err) {
        console.error('❌ Delete error:', err);
        throw err;
    }
}

// ตัวอย่างการใช้งาน
deleteFile('uploads/old-file.jpg');
3

Python Integration

ติดตั้ง Dependencies

Terminal
# ติดตั้ง boto3 (AWS SDK for Python)
pip install boto3 python-dotenv

สร้าง S3 Client

spaces_client.py
import os
import boto3
from botocore.client import Config
from dotenv import load_dotenv

load_dotenv()

# สร้าง S3 client สำหรับ DigitalOcean Spaces
session = boto3.session.Session()

s3_client = session.client(
    's3',
    region_name=os.getenv('DO_SPACES_REGION'),
    endpoint_url=os.getenv('DO_SPACES_ENDPOINT'),
    aws_access_key_id=os.getenv('DO_SPACES_KEY'),
    aws_secret_access_key=os.getenv('DO_SPACES_SECRET'),
    config=Config(s3={'addressing_style': 'virtual'})
)

BUCKET_NAME = os.getenv('DO_SPACES_BUCKET')

อัปโหลดไฟล์

upload.py
import os
from spaces_client import s3_client, BUCKET_NAME

def upload_file(local_path, remote_key, public=True):
    """
    อัปโหลดไฟล์ไปยัง DigitalOcean Spaces

    Args:
        local_path: พาธไฟล์ในเครื่อง
        remote_key: ชื่อไฟล์บน Spaces (เช่น 'images/profile.jpg')
        public: True สำหรับ public-read, False สำหรับ private
    """
    extra_args = {
        'ContentType': 'image/jpeg'  # ปรับตามประเภทไฟล์
    }

    if public:
        extra_args['ACL'] = 'public-read'

    try:
        s3_client.upload_file(
            local_path,
            BUCKET_NAME,
            remote_key,
            ExtraArgs=extra_args
        )

        url = f"https://{BUCKET_NAME}.sgp1.digitaloceanspaces.com/{remote_key}"
        print(f"✅ Upload successful! URL: {url}")
        return url

    except Exception as e:
        print(f"❌ Upload error: {e}")
        raise

# ตัวอย่างการใช้งาน
if __name__ == "__main__":
    upload_file("./local-image.jpg", "uploads/profile.jpg")

ดาวน์โหลดไฟล์

download.py
from spaces_client import s3_client, BUCKET_NAME

def download_file(remote_key, local_path):
    """ดาวน์โหลดไฟล์จาก DigitalOcean Spaces"""
    try:
        s3_client.download_file(
            BUCKET_NAME,
            remote_key,
            local_path
        )
        print(f"✅ Downloaded: {remote_key} → {local_path}")

    except Exception as e:
        print(f"❌ Download error: {e}")
        raise

# ตัวอย่างการใช้งาน
download_file("uploads/profile.jpg", "./downloaded.jpg")

แสดงรายการไฟล์

list_files.py
from spaces_client import s3_client, BUCKET_NAME

def list_files(prefix=''):
    """แสดงรายการไฟล์ใน bucket"""
    try:
        response = s3_client.list_objects_v2(
            Bucket=BUCKET_NAME,
            Prefix=prefix
        )

        print(f"📁 Files in bucket ({response['KeyCount']} items):")

        for obj in response.get('Contents', []):
            size_kb = obj['Size'] / 1024
            print(f"  - {obj['Key']} ({size_kb:.2f} KB)")

        return response.get('Contents', [])

    except Exception as e:
        print(f"❌ List error: {e}")
        raise

# ตัวอย่างการใช้งาน
list_files("uploads/")

สร้าง Presigned URL

presigned_url.py
from spaces_client import s3_client, BUCKET_NAME

def generate_presigned_url(remote_key, expiration=3600):
    """
    สร้าง URL ชั่วคราวสำหรับดาวน์โหลดไฟล์ private

    Args:
        remote_key: ชื่อไฟล์บน Spaces
        expiration: เวลาหมดอายุเป็นวินาที (default: 1 ชั่วโมง)
    """
    try:
        url = s3_client.generate_presigned_url(
            'get_object',
            Params={
                'Bucket': BUCKET_NAME,
                'Key': remote_key
            },
            ExpiresIn=expiration
        )
        print(f"🔗 Presigned URL (valid for {expiration}s):")
        print(url)
        return url

    except Exception as e:
        print(f"❌ Error: {e}")
        raise

# ตัวอย่างการใช้งาน
generate_presigned_url("private/secret.pdf", expiration=300)  # 5 นาที

Use Case: Presigned URL เหมาะสำหรับแชร์ไฟล์ private ชั่วคราว เช่น ให้ผู้ใช้ดาวน์โหลดใบเสร็จ PDF ที่หมดอายุใน 10 นาที

CDN Configuration

DigitalOcean Spaces มี Built-in CDN ที่ช่วยเร่งความเร็วในการโหลดไฟล์ จากทั่วโลก โดยอัตโนมัติ

ความเร็วสูง

  • • Edge locations ทั่วโลก
  • • ลด latency สำหรับผู้ใช้ในไทย
  • • Cache อัตโนมัติ

ความปลอดภัย

  • • SSL/TLS อัตโนมัติ
  • • DDoS protection
  • • Custom domain support

ตั้งค่า Custom Domain + CDN

  1. 1 ไปที่ Spaces → Settings → CDN
  2. 2 เพิ่ม Custom Domain: cdn.yourdomain.com
  3. 3 เพิ่ม CNAME record ใน DNS:
DNS Record
Type:  CNAME
Name:  cdn
Value: my-bucket.sgp1.cdn.digitaloceanspaces.com
TTL:   3600

ตัวอย่าง URL

Default CDN URL:

https://my-bucket.sgp1.cdn.digitaloceanspaces.com/images/photo.jpg

Custom Domain:

https://cdn.yourdomain.com/images/photo.jpg

Express.js + Multer Integration

ตัวอย่างการสร้าง API สำหรับอัปโหลดไฟล์ไปยัง DigitalOcean Spaces ด้วย Express.js และ Multer

server.js
import express from 'express';
import multer from 'multer';
import { S3Client } from '@aws-sdk/client-s3';
import multerS3 from 'multer-s3';
import dotenv from 'dotenv';

dotenv.config();

const app = express();

// สร้าง S3 Client
const s3 = new S3Client({
    region: 'us-east-1',
    endpoint: process.env.DO_SPACES_ENDPOINT,
    credentials: {
        accessKeyId: process.env.DO_SPACES_KEY,
        secretAccessKey: process.env.DO_SPACES_SECRET
    }
});

// กำหนดค่า Multer สำหรับอัปโหลดไปยัง Spaces
const upload = multer({
    storage: multerS3({
        s3: s3,
        bucket: process.env.DO_SPACES_BUCKET,
        acl: 'public-read',
        key: function (req, file, cb) {
            const fileName = `uploads/${Date.now()}-${file.originalname}`;
            cb(null, fileName);
        },
        contentType: function (req, file, cb) {
            cb(null, file.mimetype);
        }
    }),
    limits: {
        fileSize: 5 * 1024 * 1024 // 5MB max
    }
});

// API สำหรับอัปโหลดไฟล์เดียว
app.post('/upload', upload.single('file'), (req, res) => {
    if (!req.file) {
        return res.status(400).json({ error: 'No file uploaded' });
    }

    res.json({
        message: '✅ Upload successful!',
        url: req.file.location,
        key: req.file.key
    });
});

// API สำหรับอัปโหลดหลายไฟล์ (สูงสุด 10 ไฟล์)
app.post('/upload-multiple', upload.array('files', 10), (req, res) => {
    const files = req.files.map(file => ({
        url: file.location,
        key: file.key
    }));

    res.json({
        message: `✅ Uploaded ${files.length} files!`,
        files: files
    });
});

// เริ่ม server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
    console.log(`🚀 Server running on port ${PORT}`);
});

ทดสอบด้วย curl

Terminal
# อัปโหลดไฟล์เดียว
curl -X POST -F "file=@./photo.jpg" http://localhost:3000/upload

# อัปโหลดหลายไฟล์
curl -X POST -F "files=@./photo1.jpg" -F "files=@./photo2.jpg" http://localhost:3000/upload-multiple

Pricing (2025)

Plan Storage Bandwidth Price
Basic 250 GB 1 TB $5/month
Professional 1 TB 4 TB $20/month
Business 10 TB 40 TB $180/month
Overage $0.02/GB $0.01/GB Extra usage

เปรียบเทียบกับ AWS S3: DigitalOcean Spaces มีราคาถูกกว่าประมาณ 40-50% และรวม CDN ในตัวแล้ว ไม่ต้องจ่ายเพิ่ม

Use Cases ในประเทศไทย

E-commerce

เก็บรูปสินค้า รูปหมวดหมู่ และไฟล์ PDF สำหรับคู่มือสินค้า

  • • รูปสินค้าหลายขนาด (thumbnail, medium, large)
  • • ไฟล์ PDF ใบเสร็จ/ใบกำกับภาษี
  • • วิดีโอสาธิตสินค้า

Media Streaming

เก็บวิดีโอออนไลน์คอร์ส พอดแคสต์ และไฟล์เสียง

  • • วิดีโอคอร์สเรียนออนไลน์
  • • ไฟล์เสียงพอดแคสต์
  • • ไฟล์ subtitle หลายภาษา

Government/Enterprise

เก็บเอกสารราชการ แบบฟอร์ม PDF และไฟล์ข้อมูล

  • • เอกสาร PDF ดาวน์โหลด
  • • แบบฟอร์มต่างๆ
  • • ไฟล์ข้อมูล CSV/Excel

Mobile Apps

เก็บไฟล์ที่ผู้ใช้อัปโหลดจากแอปมือถือ

  • • รูปโปรไฟล์ผู้ใช้
  • • รูปสลิปโอนเงิน
  • • เอกสารแนบต่างๆ

แนะนำ: เลือก Region Singapore (sgp1) สำหรับผู้ใช้ในประเทศไทย เพราะ latency ต่ำที่สุด (ประมาณ 20-30ms)

Troubleshooting

Error: "Access Denied"

สาเหตุ: API Key หรือ Secret ไม่ถูกต้อง

แก้ไข: ตรวจสอบว่า DO_SPACES_KEY และ DO_SPACES_SECRET ตรงกับที่สร้างไว้ และไม่มีช่องว่าง extra spaces

Error: "Bucket Already Exists"

สาเหตุ: ชื่อ bucket ซ้ำกับ bucket อื่น

แก้ไข: ใช้ชื่อ bucket ที่ unique มากขึ้น เช่น mycompany-app-prod-2024

Error: "Network Timeout"

สาเหตุ: ไฟล์ใหญ่เกินไป หรือ network ช้า

แก้ไข: ใช้ multipart upload สำหรับไฟล์ใหญ่กว่า 100MB หรือเพิ่ม timeout ใน configuration

Error: "Invalid Endpoint"

สาเหตุ: Endpoint URL ไม่ถูกต้อง

แก้ไข: ตรวจสอบว่า endpoint เป็น https://sgp1.digitaloceanspaces.com (ต้องมี https://)

สรุป

ข้อดีของ DigitalOcean Spaces

  • S3-Compatible - ใช้ AWS SDK ที่มีอยู่ได้ทันที
  • รวม CDN ในตัว - เร่งความเร็วได้ทั่วโลก
  • ราคาประหยัด - เริ่ม $5/เดือน
  • Region Singapore - latency ต่ำในไทย

เหมาะสำหรับ

  • Startups และ SME ในไทย
  • เว็บไซต์ที่มี static assets มาก
  • Mobile apps ที่ต้องการ file storage
  • DevOps ที่ต้องการ backup storage

อ่านเพิ่มเติมที่ DigitalOcean Spaces Documentation