ما هو S3؟
Amazon S3 (Simple Storage Service) خدمة تخزين ملفات على سحابة Amazon. تحفظ أي نوع من الملفات (صور، فيديو، نسخ احتياطية، ملفات عامّة) بأيّ حجم وبسعر منخفض.
لماذا S3 وليس خادمك:
- لا حدود للسعة (بايلات؟ لا مشكلة)
- نسخ احتياطية تلقائية (99.999999999% متانة)
- توزيع عالمي (CDN مع CloudFront)
- أسعار معقولة ($0.023/GB شهرياً)
المفاهيم
- Bucket: "مجلد" كبير — يحوي الملفات. اسمه فريد على مستوى العالم
- Object: الملف نفسه — نص + metadata
- Key: مسار الملف داخل الـ bucket (مثل
images/profile.jpg) - Region: المنطقة الجغرافية (us-east-1، eu-west-1، me-south-1 للشرق الأوسط)
إنشاء Bucket
- اذهب لـ console.aws.amazon.com/s3
- Create bucket
- اسم فريد (مثل
my-app-uploads-2026) - Region الأقرب لمستخدميك
- Block all public access — موصى به، سنستخدم presigned URLs
IAM: مستخدم بصلاحيات محدودة
لا تستخدم root account أبداً. أنشئ مستخدماً مخصّصاً:
- IAM → Users → Add user
- Access type: Access key
- Attach policy مثل
AmazonS3FullAccess(أو أضيق للإنتاج) - احفظ Access Key ID و Secret Access Key
⚠️ لا ترفع هذه المفاتيح لـ Git أبداً.
الاتصال من Node.js
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner// s3.js
import { S3Client } from "@aws-sdk/client-s3";
export const s3 = new S3Client({
region: "me-south-1",
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
},
});
export const BUCKET = process.env.S3_BUCKET;رفع ملف
import { PutObjectCommand } from "@aws-sdk/client-s3";
import { readFile } from "node:fs/promises";
import { s3, BUCKET } from "./s3.js";
async function uploadFile(localPath, key) {
const body = await readFile(localPath);
await s3.send(new PutObjectCommand({
Bucket: BUCKET,
Key: key,
Body: body,
ContentType: "image/jpeg",
CacheControl: "public, max-age=31536000",
}));
return `https://${BUCKET}.s3.me-south-1.amazonaws.com/${key}`;
}
// الاستخدام
const url = await uploadFile("./photo.jpg", "uploads/2026/photo.jpg");رفع من متصفّح: Presigned URLs
الطريقة الأذكى — لا تمرّ الملفات بخادمك، ترفع مباشرة لـ S3.
على الخادم: أنشئ رابطاً مؤقّتاً
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { PutObjectCommand } from "@aws-sdk/client-s3";
app.post("/api/upload-url", auth, async (req, res) => {
const { filename, contentType } = req.body;
const key = `uploads/${req.user.id}/${Date.now()}-${filename}`;
const command = new PutObjectCommand({
Bucket: BUCKET,
Key: key,
ContentType: contentType,
});
const url = await getSignedUrl(s3, command, { expiresIn: 300 }); // 5 دقائق
res.json({
uploadUrl: url,
finalUrl: `https://${BUCKET}.s3.me-south-1.amazonaws.com/${key}`,
});
});على العميل: ارفع مباشرة
async function uploadToS3(file) {
// 1. اطلب presigned URL
const { uploadUrl, finalUrl } = await fetch("/api/upload-url", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ filename: file.name, contentType: file.type }),
}).then(r => r.json());
// 2. ارفع الملف مباشرة لـ S3
await fetch(uploadUrl, {
method: "PUT",
headers: { "Content-Type": file.type },
body: file,
});
return finalUrl;
}الفوائد: خادمك لا يرى الملف أبداً — أسرع ومقاوم للتحميل الثقيل.
تحميل ملف
import { GetObjectCommand } from "@aws-sdk/client-s3";
const data = await s3.send(new GetObjectCommand({
Bucket: BUCKET,
Key: "path/to/file.jpg",
}));
const bytes = await data.Body.transformToByteArray();للتحميل مباشرة من المتصفّح، أنشئ presigned GET URL بنفس الطريقة السابقة.
حذف
import { DeleteObjectCommand } from "@aws-sdk/client-s3";
await s3.send(new DeleteObjectCommand({
Bucket: BUCKET,
Key: "path/to/file.jpg",
}));التحكّم بالصلاحيات
Bucket Policy للقراءة العامّة
إن كنت تخزّن صور موقع عامّة:
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "PublicRead",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::YOUR_BUCKET/public/*"
}]
}فقط المسار public/* سيكون عامّاً. البقية تتطلّب صلاحية.
CORS لرفع مباشر من المتصفّح
[
{
"AllowedHeaders": ["*"],
"AllowedMethods": ["PUT", "POST"],
"AllowedOrigins": ["https://yoursite.com"],
"ExposeHeaders": ["ETag"]
}
]تحسين التكاليف
S3 له عدّة طبقات سعرية:
| الطبقة | السعر/GB شهرياً | الاستخدام | |--------|-----------------|-----------| | Standard | $0.023 | ملفات نشطة | | Standard-IA | $0.0125 | وصول نادر | | Glacier | $0.004 | أرشيف طويل المدى | | Deep Archive | $0.00099 | نسخ قديمة جداً |
انقل الملفات القديمة تلقائياً بـ Lifecycle rules.
CloudFront: CDN عالمي
لتسريع الوصول عالمياً، أضف CloudFront أمام الـ bucket:
- CloudFront → Create distribution
- Origin: اختر الـ bucket
- Cache behavior: افتراضي مناسب للأغلبية
- احصل على domain مثل
d123abc.cloudfront.net
الفوائد: سرعة أعلى 10x، تكاليف أقل، شهادة SSL مجانية.
بدائل أرخص
- Cloudflare R2: مشابه S3 بسعر أرخص وبدون egress fees ($0.015/GB)
- Backblaze B2: $0.005/GB — الأرخص
- DigitalOcean Spaces: مشابه S3، واجهة أبسط
الكود تقريباً نفسه — فقط غيّر الـ endpoint.
أخطاء شائعة
"Access Denied"
- تحقّق من IAM policy
- تحقّق من bucket policy
- انتبه لـ Block Public Access (قد يتجاوز الصلاحيات)
"SignatureDoesNotMatch"
- صحّح الوقت على جهازك (NTP)
- تحقّق من أنك تستخدم secret key صحيحاً
رفع بطيء
- استخدم presigned URLs (رفع مباشر)
- اختر Region قريباً من مستخدميك
- استخدم Multipart Upload للملفات > 100MB
الأسئلة الشائعة
هل أحتاج AWS كاملاً؟
لا — يمكن استخدام S3 فقط بسهولة. الحساب المجاني يُعطيك 5GB شهرياً.
S3 vs serving من الخادم؟
S3 أفضل دائماً للملفات — أسرع، أرخص، لا يحمّل خادمك، CDN جاهز.
هل أستخدم S3 لقواعد البيانات؟
لا — S3 ليست قاعدة بيانات. للبيانات المنظّمة استخدم RDS/Postgres. S3 للملفات فقط.