SDK
SDK สำหรับ Node.js, Python และ Go ทำทุกอย่างด้านล่างให้เอง ทั้งการเซ็น request การตรวจความถูกต้องของข้อมูล การลองใหม่ และการอัปโหลด/ดาวน์โหลดไฟล์ใหญ่เป็นส่วน ๆ พร้อมกัน ติดตั้งตามภาษาที่ใช้:
| ภาษา | ต้องใช้ | ติดตั้ง |
|---|---|---|
| Node.js | 18.17+ | npm install @thailandhosting/object-storage |
| Python | 3.9+ | pip install thailandhosting-object-storage |
| Go | 1.23+ | go get github.com/mrzack99s/thailandhosting-object-storage-sdk-go |
สร้าง client ด้วย Endpoint ของ Region ที่ Bucket อยู่ และ Access key จาก Console เก็บ Key ไว้ใน environment variable ไม่ใช่ในโค้ด และใช้ client ตัวเดียวตลอดโปรแกรม เพราะมันเปิดการเชื่อมต่อค้างไว้ให้ใช้ซ้ำ
import { Client, NotFoundError } from '@thailandhosting/object-storage';
const client = new Client({
endpoint: 'https://objects.thailandhosting.com/<region>',
accessKeyId: process.env.TH_ACCESS_KEY_ID,
secretAccessKey: process.env.TH_SECRET_ACCESS_KEY,
});
const bucket = client.bucket('my-bucket');
// Any size: large files go up and come down in parallel parts
await bucket.uploadFile('backups/db.dump', '/var/backups/db.dump');
await bucket.downloadFile('backups/db.dump', '/tmp/db.dump');
// Small files
await bucket.put('notes/hello.txt', 'Hello', { contentType: 'text/plain' });
console.log(await bucket.getText('notes/hello.txt'));
// Every file under a folder
for await (const obj of bucket.objects('backups/')) console.log(obj.key, obj.size);
// A link that works without a key for one hour
const link = await bucket.createLink('backups/db.dump', { expiresIn: 3600 });
console.log(link.url);
try {
await bucket.head('missing.txt');
} catch (e) {
if (e instanceof NotFoundError) console.log(e.code); // object_not_found
}import os
from thailandhosting_objectstorage import Client, NotFoundError
client = Client(
endpoint="https://objects.thailandhosting.com/<region>",
access_key_id=os.environ["TH_ACCESS_KEY_ID"],
secret_access_key=os.environ["TH_SECRET_ACCESS_KEY"],
)
bucket = client.bucket("my-bucket")
# Any size: large files go up and come down in parallel parts
bucket.upload_file("backups/db.dump", "/var/backups/db.dump")
bucket.download_file("backups/db.dump", "/tmp/db.dump")
# Small files
bucket.put("notes/hello.txt", "Hello", content_type="text/plain")
print(bucket.get_bytes("notes/hello.txt").decode())
# Every file under a folder
for obj in bucket.iter_objects("backups/"):
print(obj.key, obj.size)
# A link that works without a key for one hour
print(bucket.create_link("backups/db.dump", expires_in=3600).url)
try:
bucket.head("missing.txt")
except NotFoundError as e:
print(e.code) # object_not_foundpackage main
import (
"context"
"fmt"
"log"
"os"
"time"
objectstorage "github.com/mrzack99s/thailandhosting-object-storage-sdk-go"
)
func main() {
client, err := objectstorage.New(objectstorage.Config{
Endpoint: "https://objects.thailandhosting.com/<region>",
AccessKeyID: os.Getenv("TH_ACCESS_KEY_ID"),
SecretAccessKey: os.Getenv("TH_SECRET_ACCESS_KEY"),
})
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
bucket := client.Bucket("my-bucket")
// Any size: large files go up and come down in parallel parts
if _, err := bucket.UploadFile(ctx, "backups/db.dump", "/var/backups/db.dump", nil); err != nil {
log.Fatal(err)
}
if _, err := bucket.DownloadFile(ctx, "backups/db.dump", "/tmp/db.dump"); err != nil {
log.Fatal(err)
}
// Every file under a folder
for obj, err := range bucket.Objects(ctx, "backups/") {
if err != nil {
log.Fatal(err)
}
fmt.Println(obj.Key, obj.Size)
}
// A link that works without a key for one hour
link, err := bucket.CreateLink(ctx, "backups/db.dump", &objectstorage.LinkOptions{Expires: time.Hour})
if err != nil {
log.Fatal(err)
}
fmt.Println(link.URL)
}SDK ยังตั้งโฟลเดอร์สาธารณะ บอกที่อยู่ของไฟล์ และเขียนไฟล์เฉพาะเมื่อยังไม่มี หรือเมื่อยังไม่มีใครแก้ตั้งแต่คุณอ่านได้ด้วย:
// Anyone may read images/ without a key (the key must manage buckets)
await bucket.setPublicAccess([{ prefix: 'images/' }]);
console.log(bucket.publicUrl('images/logo.png'));
// Create a file only if it does not exist yet
await bucket.put('locks/job-42', 'me', { ifNoneMatch: '*' });
// Change it only if nobody changed it since you read it
const info = await bucket.head('config.json');
await bucket.put('config.json', next, { ifMatch: info.etag });Request ที่ล้มเพราะระบบไม่ว่างชั่วครู่หรือเน็ตหลุดจะลองใหม่ให้เอง ทุกการอัปโหลดตรวจกับ digest และการอัปโหลดไฟล์ใหญ่ที่ล้มจะถูกยกเลิกให้ ส่วนที่ค้างจึงไม่ถูกคิดเงิน
เครื่องมือ S3
AWS CLI, rclone และแอปที่รองรับ S3 ใช้ได้ทันที แค่ชี้ไปที่ Endpoint ใช้ Access key ของคุณ และตั้ง region เป็น Region ของ Bucket
aws configure set aws_access_key_id YOUR_KEY_ID
aws configure set aws_secret_access_key YOUR_SECRET
aws s3 cp backup.tar.gz s3://your-bucket/ --endpoint-url https://objects.thailandhosting.com --region <region>เรียก API โดยตรง
API เป็น HTTP และ JSON ธรรมดาอยู่ใต้ /v1 ของ Endpoint และใช้มาตรฐานของ IETF แทนรูปแบบที่คิดขึ้นเอง
| เรื่อง | ทำอย่างไร |
|---|---|
| การยืนยันตัวตน | ทุก request เซ็นด้วย HTTP Message Signatures (RFC 9421) โดยใช้ Secret ของคุณ Secret จึงไม่ถูกส่งออกไปเลย เวลาของเครื่องต้องห่างจากของเราไม่เกิน 5 นาที |
| ความถูกต้องของข้อมูล | การอัปโหลดแนบ Content-Digest (RFC 9530) เราตรวจข้อมูลกับค่านี้และปฏิเสธถ้าไม่ตรง ส่วนการดาวน์โหลดจะได้ Repr-Digest กลับไปให้ตรวจได้เช่นกัน |
| อัปเดตอย่างปลอดภัย | If-None-Match: * สร้างไฟล์เฉพาะเมื่อยังไม่มี และ If-Match แก้หรือลบไฟล์เฉพาะเมื่อยังไม่มีใครแก้หลังจากที่คุณอ่าน (RFC 9110) |
| Error | Problem Details (RFC 9457) เป็น JSON ที่มี code เช่น bucket_not_found |
อัปโหลดครั้งเดียวหรือแต่ละส่วนได้ไม่เกิน 5 GB ไฟล์ใหญ่แบ่งได้สูงสุด 10,000 ส่วน แต่ละ Project มีได้สูงสุด 100 Bucket และ 50 Access key
ถ้าเจอปัญหา
Request ไม่ผ่านและขึ้น “invalid_signature” พูดถึงเวลาของเครื่อง
นาฬิกาของเครื่องเพี้ยนเกิน 5 นาที ให้เปิดตั้งเวลาอัตโนมัติ (NTP) แล้วลองใหม่
เครื่องมือ S3 ขึ้น “SignatureDoesNotMatch”
ตรวจ Secret และตั้ง region เป็น Region ของ Bucket ไม่ใช่ region ของ cloud อื่น
อัปโหลดไม่ผ่าน ขึ้น 412 “precondition_failed”
คุณขอให้สร้างไฟล์เฉพาะเมื่อยังไม่มี หรือแก้เฉพาะเมื่อยังไม่ถูกแก้ แต่ไฟล์มีอยู่แล้วหรือถูกแก้ไปแล้ว ให้อ่านไฟล์ใหม่แล้วตัดสินใจว่าจะเขียนทับหรือไม่
บางครั้ง request ไม่ผ่านด้วย 503 “busy” หรือ “SlowDown”
ระบบไม่ว่างชั่วครู่ ให้รอตามจำนวนวินาทีใน header Retry-After แล้วลองใหม่ SDK และเครื่องมือ S3 ทำให้เองอยู่แล้ว