تخطي إلى المحتوى الرئيسي

بروتوكول tus

tus هو بروتوكول مفتوح للرفع القابل للاستئناف. استخدمه للرفع الموثوق الذي يمكنه النجاة من انقطاعات الشبكة.

لماذا tus؟

  • قابل للاستئناف: استمر في الرفع بعد فشل الشبكة
  • فعال: استأنف من حيث توقفت، وليس من البداية
  • موحد: بروتوكول محدد جيداً مع العديد من تطبيقات الخادم
  • مجزأ: يعالج الملفات الكبيرة تلقائياً

التثبيت

npm install @samithahansaka/dropup tus-js-client

tus-js-client هي تبعية نظير.

الاستخدام الأساسي

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>أفلت الملفات للرفع القابل للاستئناف</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/',

// حجم الجزء (الافتراضي: Infinity = بدون تجزئة)
chunkSize: 5 * 1024 * 1024, // أجزاء 5MB

// تكوين إعادة المحاولة
retryDelays: [0, 1000, 3000, 5000], // إعادة المحاولة بعد 0ث، 1ث، 3ث، 5ث

// ترويسات لجميع الطلبات
headers: {
'Authorization': 'Bearer token',
},

// بيانات وصفية مرسلة للخادم
metadata: {
filename: 'file.name',
filetype: 'file.type',
// إضافة بيانات وصفية مخصصة
userId: 'user123',
},

// تمكين قابلية الاستئناف عبر localStorage
storeFingerprintForResuming: true,

// إزالة البصمة بعد الرفع الناجح
removeFingerprintOnSuccess: true,

// رفع متوازي
parallelUploads: 3,
});

استئناف الرفع السابق

يمكن لـ tus استئناف الرفع المنقطع:

const { files, actions } = useDropup({
upload: createTusUploader({
endpoint: 'https://your-tus-server.com/files/',

// تخزين روابط الرفع للاستئناف
storeFingerprintForResuming: true,

// اختياري: مفتاح تخزين مخصص
urlStorageKey: 'my-app-tus-uploads',

onShouldRetry: (error, retryAttempt) => {
// منطق إعادة المحاولة المخصص
if (error.message.includes('network')) {
return retryAttempt < 5;
}
return false;
},
}),
});

// يمكن استئناف الملفات تلقائياً عند إعادة إضافتها
// أو يدوياً باستخدام 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
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));

معالجة البيانات الوصفية المخصصة

// جانب الخادم: الوصول للبيانات الوصفية
const server = new Server({
path: '/files',
datastore: new FileStore({ directory: './uploads' }),
onUploadCreate: async (req, res, upload) => {
// الوصول للبيانات الوصفية المرسلة من العميل
console.log(upload.metadata);
// { filename: 'photo.jpg', filetype: 'image/jpeg', userId: 'user123' }
},
onUploadFinish: async (req, res, upload) => {
// معالجة الرفع المكتمل
console.log('اكتمل الرفع:', upload.id);
},
});

امتدادات tus

تكامل tus في Dropup يدعم هذه الامتدادات:

الامتدادالوصف
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, // أجزاء 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 مقابل الرفع المجزأ

الميزةtusمجزأ مخصص
قابلية الاستئنافمدمجةيدوية
دعم الخادمخيارات متعددةمخصص
حمل البروتوكولأعلىأقل
التكوينموحدمرن
الأفضل لـملفات كبيرة، شبكات غير موثوقةرفع بسيط