تتضمن معالجة تحميلات الملفات بشكل صحيح أكثر من مجرد قبول ملف، فأنت تحتاج إلى التحقق من الصحة واستراتيجية التخزين والأمان ومعالجة الصور في كثير من الأحيان. يغطي هذا الدليل عمليات تحميل الملفات القوية في Node.js بدءًا من التخزين المحلي البسيط وحتى التحميلات السحابية القابلة للتطوير.
📋 Table of Contents
نظرة عامة على استراتيجيات التحميل
| استراتيجية | الأفضل لـ |
|---|---|
| تخزين القرص المحلي | تطبيقات صغيرة، خادم واحد |
| قم بالرفع إلى الخادم الخاص بك، ثم على السحابة | المعالجة اللازمة قبل التخزين |
| مباشر إلى السحابة (عناوين URL المميزة) | الملفات الكبيرة، الحجم، أفضل الممارسات |
التحميل الأساسي باستخدام Multer
npm install multer
const multer = require('multer');
const path = require('path');
const storage = multer.diskStorage({
destination: './uploads',
filename: (req, file, cb) => {
// Generate a safe, unique filename - never trust the client's name
const unique = Date.now() + '-' + Math.round(Math.random() * 1e9);
cb(null, unique + path.extname(file.originalname));
},
});
const upload = multer({
storage,
limits: { fileSize: 5 * 1024 * 1024 }, // 5MB limit
fileFilter: (req, file, cb) => {
// Only allow specific types
const allowed = ['image/jpeg', 'image/png', 'image/webp'];
if (allowed.includes(file.mimetype)) {
cb(null, true);
} else {
cb(new Error('Only JPEG, PNG, and WebP images allowed'));
}
},
});
app.post('/upload', upload.single('file'), (req, res) => {
res.json({ filename: req.file.filename, size: req.file.size });
});
التحقق من صحة الملفات بشكل صحيح (لا تثق بنوع MIME وحده)
npm install file-type
const { fileTypeFromBuffer } = require('file-type');
const fs = require('fs').promises;
// The client-provided MIME type can be faked - verify actual content
async function validateFile(filePath) {
const buffer = await fs.readFile(filePath);
const type = await fileTypeFromBuffer(buffer);
const allowed = ['image/jpeg', 'image/png', 'image/webp'];
if (!type || !allowed.includes(type.mime)) {
await fs.unlink(filePath); // delete the invalid file
throw new Error('Invalid file content');
}
return type;
}
ملفات وحقول متعددة
// Multiple files under one field
app.post('/gallery', upload.array('photos', 10), (req, res) => {
const files = req.files.map(f => ({ filename: f.filename, size: f.size }));
res.json({ uploaded: files });
});
// Different fields
const uploadFields = upload.fields([
{ name: 'avatar', maxCount: 1 },
{ name: 'documents', maxCount: 5 },
]);
app.post('/profile', uploadFields, (req, res) => {
const avatar = req.files.avatar?.[0];
const docs = req.files.documents || [];
res.json({ avatar: avatar?.filename, docCount: docs.length });
});
معالجة الصور ببرنامج شارب
npm install sharp
const sharp = require('sharp');
// Resize and optimize uploaded images
async function processImage(inputPath, outputPath) {
await sharp(inputPath)
.resize(1200, 1200, { fit: 'inside', withoutEnlargement: true })
.webp({ quality: 80 }) // convert to WebP, compress
.toFile(outputPath);
}
// Generate a thumbnail
async function makeThumbnail(inputPath, thumbPath) {
await sharp(inputPath)
.resize(200, 200, { fit: 'cover' })
.toFile(thumbPath);
}
التحميل المباشر إلى السحابة باستخدام عناوين URL المحددة (الأفضل من حيث النطاق)
بالنسبة للملفات كبيرة الحجم وحجمها الكبير، لا تقم بتوجيه التحميلات عبر الخادم الخاص بك – اسمح للعملاء بالتحميل مباشرة إلى S3 باستخدام عنوان URL محدد مسبقًا تنشئه الواجهة الخلفية الخاصة بك:
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
const { getSignedUrl } = require('@aws-sdk/s3-request-presigner');
const s3 = new S3Client({ region: 'us-east-1' });
// Backend: generate a presigned upload URL
app.post('/upload-url', async (req, res) => {
const { filename, contentType } = req.body;
const key = `uploads/${Date.now()}-${filename}`;
const command = new PutObjectCommand({
Bucket: 'my-bucket',
Key: key,
ContentType: contentType,
});
const uploadUrl = await getSignedUrl(s3, command, { expiresIn: 300 });
res.json({ uploadUrl, key });
});
// Frontend: upload directly to S3, bypassing your server
const { uploadUrl, key } = await fetch('/upload-url', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ filename: file.name, contentType: file.type }),
}).then(r => r.json());
await fetch(uploadUrl, {
method: 'PUT',
headers: { 'Content-Type': file.type },
body: file, // uploads straight to S3 - no server bottleneck
});
قائمة التحقق من الأمان
- التحقق من صحة نوع الملف حسب المحتوى، وليس فقط نوع MIME الخاص بالعميل (والذي يمكن تزييفه)
- تحديد حجم الملف في كل طبقة (multer، nginx، cloud)
- إنشاء أسماء ملفات آمنة – لا تستخدم أبدًا اسم ملف العميل مباشرةً (خطر اجتياز المسار)
- تخزين خارج جذر الويب أو في التخزين السحابي، وليس في دليل يمكن تقديمه بشكل عام ويمكنه تنفيذ عمليات التحميل
- البحث عن البرامج الضارة في حالة قبول الملفات من مستخدمين غير موثوق بهم
- قم بتعيين نوع المحتوى المناسب وترتيب المحتوى عند تقديم الملفات مرة أخرى
الأسئلة المتداولة
س: هل يجب أن تتم التحميلات عبر الخادم الخاص بي أم مباشرة إلى التخزين السحابي؟
ج: بالنسبة للملفات الصغيرة أو عندما تحتاج إلى معالجتها أولاً، من خلال الخادم الخاص بك (متعدد). بالنسبة للملفات الكبيرة وحجمها، استخدم عناوين URL الموقعة مسبقًا حتى يتمكن العملاء من التحميل مباشرة إلى S3 – وهذا يتجاوز النطاق الترددي لخادمك وحدود الحجم والقياس بشكل أفضل بكثير.
س: لماذا لا يجب أن أثق في نوع MIME الخاص بالعميل؟
ج: يتم التحكم في رأس Content-Type من قبل العميل ويمكن تزييفه بسهولة – يمكن للمهاجم تحميل ملف ضار مسمى كصورة. تحقق من محتوى الملف الفعلي باستخدام مكتبة مثل file-type، التي تقرأ البايتات السحرية للملف.
س: كيف أتعامل مع تحميل الملفات الكبيرة جدًا؟
ج: استخدم عناوين URL الموقعة مسبقًا للتحميل المباشر إلى السحابة (لا يوجد اختناق في الخادم)، أو قم بتنفيذ عمليات تحميل مقسمة/قابلة للاستئناف للملفات الضخمة. قم بزيادة حدود الحجم في جميع الطبقات (multer، nginx، cloud) في حالة التوجيه عبر الخادم الخاص بك، ولكن النقل المباشر إلى السحابة أفضل للملفات الكبيرة.
س: أين يجب أن أقوم بتخزين الملفات التي تم تحميلها؟
ج: تخزين الكائنات السحابية (S3، GCS، R2) لقابلية التوسع والموثوقية – وليس على قرص خادم التطبيق الخاص بك (والذي لا يتوسع عبر المثيلات ويتم فقده إذا تم استبدال الخادم). بالنسبة لتطبيقات الخادم الفردي الصغيرة، يعمل القرص المحلي ولكنه يخطط للانتقال إلى التخزين السحابي مع نموك.
س: كيف أمنع تنفيذ الملفات التي تم تحميلها؟
ج: قم بتخزين التحميلات خارج جذر الويب أو في وحدة التخزين السحابية، وقم بإنشاء أسماء ملفات عشوائية، والتحقق من صحة نوع المحتوى، وتقديم الملفات بالعناوين المناسبة (ترتيب المحتوى: مرفق للتنزيلات). لا تقم مطلقًا بتخزين تحميلات المستخدم في دليل حيث سيقوم خادم الويب بتنفيذها كبرامج نصية.
الخلاصة
تتطلب معالجة تحميلات الملفات في Node.js أكثر من مجرد قبول ملف. استخدممتعدد التحميلات من جانب الخادم مع حدود الحجم وتصفية النوع، والتحقق من صحة الملفات حسب المحتوى الفعلي (وليس نوع MIME الخاص بالعميل)، ومعالجة الصور باستخدام Sharp. بالنسبة للملفات الكبيرة الحجم والكبيرة، استخدمعناوين URL المحددة مسبقًا للسماح للعملاء بالتحميل مباشرة إلى التخزين السحابي، تجاوز الخادم الخاص بك. اتبع دائمًا ممارسات الأمان: التحقق من صحة المحتوى، والحد من الأحجام، وإنشاء أسماء ملفات آمنة، وتخزين الملفات في وحدة التخزين السحابية أو خارج جذر الويب. إن إجراء التحميلات بشكل صحيح يعني تحقيق التوازن بين الراحة والأداء والأمان – يعد النقل المباشر إلى السحابة مع التحقق المناسب من أفضل الممارسات الحديثة لمعظم التطبيقات.
🔗 Share this article
✍️ Leave a Comment