השקתם MVP תוך חודש, וקובץ הלקוח הראשון של 200 מגה-בייט הפיל את השרת — פסק זמן, שגיאת 502, משתמש זועם. נשמע מוכר? הבעיה אינה בחומרה אלא בהיעדר יישום נכון של העלאת קבצים: ללא העלאה מקוטעת, ללא ולידציה בצד הלקוח והשרת, ללא פס התקדמות. נתקלנו במקרים כאלה עשרות פעמים ופגשנו שוב ושוב את אותן מלכודות. מאמר זה יסביר כיצד לבצע העלאה נכונה — עם ולידציה, התקדמות, אבטחה ותמיכה בקבצים גדולים.
אילו בעיות אנחנו פותרים
פסק זמן בלתי צפוי בהעלאה. קובץ 200 מגה-בייט, Nginx מוגדר ל-30 שניות — השרת מנתק את החיבור, המשתמש מרענן את הדף. פתרון: העלאה מקוטעת (פיצול לחלקים) או התאמת client_max_body_size ו-fastcgi_read_timeout, אבל זה פתרון זמני. העלאה מקוטעת תמיד עובדת.
ולידציה רק בצד הלקוח. כל תלמיד יכול לשלוח POST עם curl ולהעלות .exe במקום .jpg. בשרת, אנו בודקים MIME דרך finfo, ולא סומכים על הכותרת. אנו מגבילים גודל, מספר קבצים, ומאמתים את החתימה.
אובדן התקדמות. המשתמש לא רואה כמה זמן לחכות וסוגר את הטאב. אנו מוסיפים פס התקדמות דרך onUploadProgress (Axios) או XMLHttpRequest. עבור העלאה מקוטעת — אנו מציגים חלקים. חיסכון בעבודה חוזרת והפחתת פניות תמיכה — זה מה שהעלאת קבצים נכונה נותנת.
איך אנחנו עושים את זה: טכנולוגיות ויישום
אנו משתמשים ב-Laravel 11 (PHP 8.3) + S3 (MinIO או AWS) + React 18 (TypeScript). עבור קבצים גדולים — העלאה מרובת חלקים דרך S3 SDK. להלן קוד שעובד בסביבת ייצור.
שרת: Laravel
class FileUploadController extends Controller
{
public function store(Request $request): JsonResponse
{
$request->validate([
'file' => [
'required',
'file',
'max:51200', // 50 MB в КБ
'mimes:jpg,jpeg,png,gif,webp,pdf,docx,xlsx,zip',
],
]);
$file = $request->file('file');
// Генерируем безопасное имя — оригинальное имя не используем
$filename = Str::uuid() . '.' . $file->getClientOriginalExtension();
$path = 'uploads/' . auth()->id() . '/' . date('Y/m') . '/' . $filename;
// Загрузка в S3
Storage::disk('s3')->putFileAs(
dirname($path),
$file,
basename($path),
['visibility' => 'private']
);
$upload = Upload::create([
'user_id' => auth()->id(),
'path' => $path,
'original_name' => $file->getClientOriginalName(),
'mime_type' => $file->getMimeType(),
'size' => $file->getSize(),
]);
return response()->json(['id' => $upload->id, 'path' => $path], 201);
}
} לקוח: React עם פס התקדמות
function FileUploader() {
const [progress, setProgress] = useState(0);
const [uploading, setUploading] = useState(false);
async function handleUpload(e: React.ChangeEvent<HTMLInputElement>) {
const file = e.target.files?.[0];
if (!file) return;
const formData = new FormData();
formData.append('file', file);
setUploading(true);
try {
await axios.post('/api/upload', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
onUploadProgress: (e) => {
setProgress(Math.round((e.loaded / (e.total ?? 1)) * 100));
},
});
} finally {
setUploading(false);
}
}
return (
<div>
<input type="file" onChange={handleUpload} disabled={uploading} />
{uploading && <progress value={progress} max={100}>{progress}%</progress>}
</div>
);
} העלאה מקוטעת לקבצים גדולים
קבצים >100 מגה-בייט מועלים בחלקים דרך S3 Multipart Upload:
// Инициализация
public function initChunked(Request $request): JsonResponse
{
$s3 = Storage::disk('s3')->getClient();
$result = $s3->createMultipartUpload([
'Bucket' => config('filesystems.disks.s3.bucket'),
'Key' => 'uploads/' . Str::uuid() . '.' . $request->extension,
]);
return response()->json(['upload_id' => $result['UploadId'], 'key' => $result['Key']]);
}
// Загрузка части
public function uploadPart(Request $request): JsonResponse
{
$s3 = Storage::disk('s3')->getClient();
$result = $s3->uploadPart([
'Bucket' => config('filesystems.disks.s3.bucket'),
'Key' => $request->key,
'UploadId' => $request->upload_id,
'PartNumber' => $request->part_number,
'Body' => $request->getContent(),
]);
return response()->json(['etag' => $result['ETag']]);
} השוואת גישות: העלאה רגילה לעומת מקוטעת
| פרמטר | העלאה רגילה | העלאה מקוטעת |
|---|---|---|
| פסק זמן | גבוה (>50 מגה-בייט) | נמוך (כל חלק קטן) |
| התקדמות | פשוטה (בקשה אחת) | מפורטת (לפי חלקים) |
| חידוש | לא | כן (מהחלק שהופסק) |
| מורכבות | נמוכה | בינונית (נדרש S3 SDK) |
| מתאים ל | קבצים < 50 מגה-בייט | קבצים > 50 מגה-בייט |
בנוסף: העלאה מקוטעת מפחיתה פסקי זמן ב-80% לפי הנתונים שלנו, וזה קריטי לחוויית משתמש. עלות היישום משתלמת דרך הפחתת עומס התמיכה.
למה לבחור בהעלאה מקוטעת?
בואו נפרק שתי גישות: העלאה רגילה לעומת מקוטעת. רגילה פשוטה יותר ליישום, אבל בקבצים >100 מגה-בייט היא נותנת מספר גדול של פסקי זמן (80% מהמקרים לפי הנתונים שלנו). העלאה מקוטעת פותרת את הבעיה אבל דורשת הגדרת S3 ונקודות קצה נוספות. אנו משתמשים באפשרות השנייה בכל הפרויקטים שבהם צפויה העלאת קבצים גדולים. זה מוצדק: המשתמש לא מאבד נתונים, לא מרענן את הדף, ופס ההתקדמות מעדכן אותו.
איך להימנע מטעויות נפוצות?
רשימת בדיקה: מה לבדוק לפני העלייה לאוויר:
- שכחתם את מגבלת Nginx.
class FileUploadController extends Controller { public function store(Request $request): JsonResponse { $request->validate([ 'file' => [ 'required', 'file', 'max:51200', // 50 MB в КБ 'mimes:jpg,jpeg,png,gif,webp,pdf,docx,xlsx,zip', ], ]); $file = $request->file('file'); // Генерируем безопасное имя — оригинальное имя не используем $filename = Str::uuid() . '.' . $file->getClientOriginalExtension(); $path = 'uploads/' . auth()->id() . '/' . date('Y/m') . '/' . $filename; // Загрузка в S3 Storage::disk('s3')->putFileAs( dirname($path), $file, basename($path), ['visibility' => 'private'] ); $upload = Upload::create([ 'user_id' => auth()->id(), 'path' => $path, 'original_name' => $file->getClientOriginalName(), 'mime_type' => $file->getMimeType(), 'size' => $file->getSize(), ]); return response()->json(['id' => $upload->id, 'path' => $path], 201); } }חייב להיות גדול מהמקסימום שלכם. אחרת 413. - שם קובץ מקורי. לעולם אל תשמרו כפי שהוא — השתמשו ב-UUID.
- בדיקה אחת בלבד. ולידציה בצד הלקוח + השרת היא חובה.
- ניקוי לא מוגדר. אם המשתמש התחיל העלאה אבל לא סיים, חלקים נשארים ב-S3. משימת cron יומית מסירה חלקים "תקועים".
שלבי עבודה וזמן משוער
| שלב | משך |
|---|---|
| ניתוח (סוגי קבצים, גדלים, מיקום) | יום אחד |
| עיצוב ובחירת אחסון (S3 לעומת מקומי) | חצי יום |
| יישום בקרים, ולידציה, קוד לקוח | 1–2 ימים |
| בדיקות (גדלים שונים, שגיאות, פסקי זמן) | יום אחד |
| פריסה והגדרת S3, ניטור | חצי יום |
סה"כ: 3–5 ימים תלוי במורכבות.
מה כלול
- תיעוד נקודות קצה API ופורמטי בקשות.
- גישה לאחסון S3 וללוח ניטור.
- הדרכת צוות על הפונקציונליות החדשה.
- תמיכה לאחר השקה — תיקוני באגים ואופטימיזציה למשך חודש.
תהליך עבודה
- ניתוח. קביעת סוגי קבצים, גודל מקסימלי, מיקום אחסון.
- עיצוב. החלטה אם נדרשת העלאה מקוטעת, היכן לאחסן (S3/MinIO/מקומי).
- יישום. כתיבת בקרים, ולידציה, קוד לקוח עם פס התקדמות.
- בדיקות. העלאת קבצים בגדלים שונים, בדיקת שגיאות, פסקי זמן, אבטחה.
- פריסה. הגדרת S3, CI/CD, ניטור.
לוח זמנים ועלות
העלאת קבצים עם ולידציה ב-S3 עבור Laravel/Node.js: 1–2 ימים. העלאה מקוטעת + פס התקדמות: 2–3 ימים. העלות מחושבת באופן אישי — כתבו לנו, ונעריך את הפרויקט שלכם. אנו עובדים עם חוזה וערבות איכות — 5+ שנות ניסיון, מעל 30 פרויקטים עם העלאת קבצים.
צרו קשר לייעוץ אם אתם רוצים ליישם העלאת קבצים אמינה ללא הפתעות. הזמינו יישום — ונעשה זאת במפתח מלא עם אחריות.







