tus Protocol
tus คือโปรโตคอลเปิดสำหรับการอัปโหลดไฟล์แบบ resumable ใช้สำหรับการอัปโหลดที่เชื่อถือได้ซึ่งสามารถรอดจากการขัดข้องของเครือข่าย
ทำไมต้อง tus?
- Resumable: ดำเนินการอัปโหลดต่อหลังจากเครือข่ายล้มเหลว
- มีประสิทธิภาพ: ดำเนินการต่อจากจุดที่หยุด ไม่ใช่ตั้งแต่ต้น
- มีมาตรฐาน: โปรโตคอลที่กำหนดไว้ชัดเจนพร้อม server implementations มากมาย
- แบ่งชิ้น: จัดการไฟล์ขนาดใหญ่โดยอัตโนมัติ
การติดตั้ง
npm install @samithahansaka/dropup tus-js-client
tus-js-client เป็น peer dependency
การใช้งานพื้นฐาน
import { useDropup } from '@samithahansaka/dropup';
import { createTusUploader } from '@samithahansaka/dropup/tus';
function TusUploader() {
const { files, actions, state, getDropProps, getInputProps } = useDropup({
upload: createTusUploader({
endpoint: 'https://tusd.tusdemo.net/files/',
}),
onUploadComplete: (file) => {
console.log('อัปโหลดเสร็จ:', file.uploadedUrl);
},
});
return (
<div>
<div {...getDropProps()} style={styles.dropzone}>
<input {...getInputProps()} />
<p>วางไฟล์สำหรับการอัปโหลดแบบ resumable</p>
</div>
{files.map(file => (
<div key={file.id} style={styles.fileItem}>
<span>{file.name}</span>
<span>{file.progress}%</span>
<span>{file.status}</span>
</div>
))}
<button onClick={() => actions.upload()}>
อัปโหลด
</button>
</div>
);
}
ตัวเลือกการกำหนดค่า
createTusUploader({
// จำเป็น
endpoint: 'https://your-tus-server.com/files/',
// ขนาด chunk (ค่าเริ่มต้น: Infinity = ไม่แบ่ง)
chunkSize: 5 * 1024 * 1024, // chunks ขนาด 5MB
// การกำหนดค่า retry
retryDelays: [0, 1000, 3000, 5000], // ลองใหม่หลัง 0s, 1s, 3s, 5s
// Headers สำหรับทุก requests
headers: {
'Authorization': 'Bearer token',
},
// Metadata ที่ส่งไปยังเซิร์ฟเวอร์
metadata: {
filename: 'file.name',
filetype: 'file.type',
// เพิ่ม metadata แบบกำหนดเอง
userId: 'user123',
},
// เปิดใช้งาน resumability ผ่าน localStorage
storeFingerprintForResuming: true,
// ลบ fingerprint หลังอัปโหลดสำเร็จ
removeFingerprintOnSuccess: true,
// อัปโหลดขนานกัน
parallelUploads: 3,
});
ดำเนินการต่อการอัปโหลดที่ค้างไว้
tus สามารถดำเนินการต่อการอัปโหลดที่ถูกขัดจังหวะ:
const { files, actions } = useDropup({
upload: createTusUploader({
endpoint: 'https://your-tus-server.com/files/',
// เก็บ upload URLs สำหรับ resuming
storeFingerprintForResuming: true,
// ไม่บังคับ: custom storage key
urlStorageKey: 'my-app-tus-uploads',
onShouldRetry: (error, retryAttempt) => {
// logic retry แบบกำหนดเอง
if (error.message.includes('network')) {
return retryAttempt < 5;
}
return false;
},
}),
});
// ไฟล์สามารถ resume โดยอัตโนมัติเมื่อเพิ่มใหม่
// หรือด้วยตนเองด้วย retry:
actions.retry(['file-id']);
การติดตามความคืบหน้า
function TusWithProgress() {
const { files, actions, getDropProps, getInputProps } = useDropup({
upload: createTusUploader({
endpoint: 'https://your-tus-server.com/files/',
}),
onUploadProgress: (file, progress) => {
console.log(`${file.name}: ${progress}%`);
},
onUploadStart: (file) => {
console.log(`เริ่มอัปโหลด: ${file.name}`);
},
onUploadComplete: (file) => {
console.log(`สำเร็จ: ${file.uploadedUrl}`);
},
});
return (
<div>
<div {...getDropProps()}>
<input {...getInputProps()} />
<p>วางไฟล์ที่นี่</p>
</div>
{files.map(file => (
<div key={file.id}>
<span>{file.name}</span>
{file.status === 'uploading' && (
<div style={styles.progressBar}>
<div
style={{
...styles.progress,
width: `${file.progress}%`,
}}
/>
</div>
)}
<span>{file.status}</span>
</div>
))}
</div>
);
}
หยุดชั่วคราวและดำเนินการต่อ
function PausableUploader() {
const { files, actions, getDropProps, getInputProps } = useDropup({
upload: createTusUploader({
endpoint: 'https://your-tus-server.com/files/',
storeFingerprintForResuming: true,
}),
});
return (
<div>
<div {...getDropProps()}>
<input {...getInputProps()} />
<p>วางไฟล์ที่นี่</p>
</div>
{files.map(file => (
<div key={file.id}>
<span>{file.name}</span>
<span>{file.progress}%</span>
{file.status === 'uploading' && (
<button onClick={() => actions.cancel(file.id)}>
หยุดชั่วคราว
</button>
)}
{file.status === 'paused' && (
<button onClick={() => actions.retry([file.id])}>
ดำเนินการต่อ
</button>
)}
{file.status === 'error' && (
<button onClick={() => actions.retry([file.id])}>
ลองใหม่
</button>
)}
</div>
))}
<button onClick={() => actions.upload()}>
เริ่มทั้งหมด
</button>
</div>
);
}
การตั้งค่าเซิร์ฟเวอร์
tusd (เซิร์ฟเวอร์อย่างเป็นทางการ)
# ติดตั้ง tusd
go install github.com/tus/tusd/cmd/tusd@latest
# รันด้วยตัวเลือกเริ่มต้น
tusd -upload-dir=/uploads
# รันด้วย S3 storage
tusd -s3-bucket=my-bucket -s3-endpoint=https://s3.amazonaws.com
Node.js (tus-node-server)
const { Server } = require('@tus/server');
const { FileStore } = require('@tus/file-store');
const server = new Server({
path: '/files',
datastore: new FileStore({ directory: './uploads' }),
});
// กับ Express
app.all('/files/*', server.handle.bind(server));
การจัดการ Metadata แบบกำหนดเอง
// ฝั่งเซิร์ฟเวอร์: เข้าถึง metadata
const server = new Server({
path: '/files',
datastore: new FileStore({ directory: './uploads' }),
onUploadCreate: async (req, res, upload) => {
// เข้าถึง metadata ที่ส่งจากไคลเอนต์
console.log(upload.metadata);
// { filename: 'photo.jpg', filetype: 'image/jpeg', userId: 'user123' }
},
onUploadFinish: async (req, res, upload) => {
// ประมวลผลการอัปโหลดที่เสร็จ
console.log('อัปโหลดเสร็จ:', upload.id);
},
});
tus Extensions
การผสานรวม tus ของ Dropup รองรับ tus extensions เหล่านี้:
| Extension | คำอธิบาย |
|---|---|
| Creation | สร้างการอัปโหลดใหม่ |
| Termination | ยกเลิกการอัปโหลด |
| Checksum | ตรวจสอบความถูกต้อง |
| Concatenation | อัปโหลดขนานกัน |
| Expiration | ล้างอัตโนมัติ |
การจัดการข้อผิดพลาด
const { files, actions } = useDropup({
upload: createTusUploader({
endpoint: 'https://your-tus-server.com/files/',
retryDelays: [0, 1000, 3000, 5000, 10000],
}),
onUploadError: (file, error) => {
if (error.code === 'TUS_OFFLINE') {
console.log('เครือข่ายออฟไลน์ จะดำเนินการต่อเมื่อเชื่อมต่อ');
} else if (error.code === 'TUS_EXPIRED') {
console.log('การอัปโหลดหมดอายุ เริ่มใหม่');
actions.retry([file.id]);
} else {
console.error('อัปโหลดล้มเหลว:', error.message);
}
},
});
การอัปโหลดไฟล์ขนาดใหญ่
tus เหมาะสำหรับไฟล์ขนาดใหญ่:
function LargeFileUploader() {
const { files, actions, getDropProps, getInputProps } = useDropup({
maxSize: 10 * 1024 * 1024 * 1024, // ขีดจำกัด 10GB
upload: createTusUploader({
endpoint: 'https://your-tus-server.com/files/',
chunkSize: 50 * 1024 * 1024, // chunks ขนาด 50MB
retryDelays: [0, 3000, 5000, 10000, 20000],
storeFingerprintForResuming: true,
}),
onUploadProgress: (file, progress) => {
// แสดงความคืบหน้าละเอียดสำหรับไฟล์ขนาดใหญ่
const uploadedMB = (file.size * progress / 100 / 1024 / 1024).toFixed(0);
const totalMB = (file.size / 1024 / 1024).toFixed(0);
console.log(`${uploadedMB}MB / ${totalMB}MB`);
},
});
return (
<div {...getDropProps()}>
<input {...getInputProps()} />
<p>อัปโหลดไฟล์ขนาดใหญ่ (สูงสุด 10GB)</p>
</div>
);
}
เปรียบเทียบ: tus vs Chunked Upload
| ฟีเจอร์ | tus | Custom Chunked |
|---|---|---|
| Resumability | ในตัว | Manual |
| Server support | มีหลายตัวเลือก | Custom |
| Protocol overhead | สูงกว่า | ต่ำกว่า |
| Configuration | มีมาตรฐาน | ยืดหยุ่น |
| เหมาะสำหรับ | ไฟล์ขนาดใหญ่, เครือข่ายไม่เสถียร | อัปโหลดแบบง่าย |