الرئيسيةمن أناالدوراتالمدونةسوق الأوامرالمناهج والباقاتالشركاء

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

المنصة مبنية على الوضوح، التطبيق، والنتيجة النافعة: شرح مرتب يساعدك تفهم الأدوات، تكتب كودًا أفضل، وتستخدم الذكاء الاصطناعي بوعي داخل العمل الحقيقي.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • المناهج والباقات
  • سوق الأوامر
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالمناهجالمدونةالدخول
How To Make It

ارفع الملفات مباشرة إلى S3 بروابط موقّعة: خادمك يوقّع فقط

متوسط3 أغسطس 20266 دقائق قراءة
ارفع الملفات مباشرة إلى S3 بروابط موقّعة: خادمك يوقّع فقط

يتطلب هذا المقال مستوى: متوسط. لازم تكون عارف Python أساسي، وشكل طلب HTTP، وعندك فكرة عن S3. مش محتاج تكون خبير AWS.

ارفع الملفات مباشرة إلى S3 بروابط موقّعة: خادمك يوقّع بس، والملف ميعديش عليه

لو المستخدم بيرفع ملف 240 ميجابايت وبيعدّي على خادمك الأول، انت بتدفع الثمن مرتين: ذاكرة الخادم بتتحجز طول الرفع، والـ request بيقع عند 30 ثانية. الحل إن المتصفح يرفع الملف مباشرة على S3، وخادمك دوره الوحيد إنه يوقّع رابط. في المقال ده هتبني ده خطوة بخطوة بـ boto3، وهتقيس الفرق بالأرقام.

المشكلة باختصار

الطريقة الشائعة: المتصفح يبعت الملف لخادمك (Flask أو FastAPI أو Express)، وخادمك يرفعه على S3. الطريقة دي بتفشل بمجرد ما الملفات تكبر. كل رفع بيحجز ذاكرة على الخادم بحجم الملف تقريبًا. عشرة مستخدمين بيرفعوا 240 ميجابايت في نفس الوقت معناه ~2.4 جيجابايت RAM محجوزة في لحظة واحدة. وأغلب البروكسيات (زي nginx أو الـ load balancer) بتقفل الاتصال بعد 30 أو 60 ثانية، فالرفع الكبير بيضرب 504 Gateway Timeout.

الفكرة الأول بمثال بسيط

تخيّل مطعم فيه فاليه باركنج. انت مش بتدّي مفتاح عربيتك لكل واحد واقف على الباب. بتاخد من موظف الاستقبال تذكرة عليها رقم ومكتوب إنها صالحة النهاردة بس. التذكرة دي بتخوّلك تسيب العربية في مكان محدد، من غير ما موظف الاستقبال يمسك العربية بنفسه.

الرابط الموقّع (Presigned URL) هو التذكرة دي بالظبط. خادمك عنده مفتاح AWS السري (زي مدير المطعم). بدل ما يمرّر كل ملف بنفسه، بيولّد رابطًا موقّعًا: عنوان طويل فيه توقيع تشفيري بيقول لـ S3 «الشخص اللي معاه اللينك ده مسموح له يكتب الملف ده بالتحديد في المكان ده بالتحديد، ولمدة 5 دقايق بس». المتصفح بيستخدم الرابط ويرفع مباشرة على S3.

علميًا: الرابط الموقّع هو URL عادي مضاف له معاملات توقيع بخوارزمية AWS Signature Version 4. التوقيع بيتحسب من مفتاحك السري + اسم الـ bucket + اسم الملف (key) + وقت الانتهاء + العملية (PUT). S3 بيعيد حساب نفس التوقيع لما الطلب يوصله؛ لو طابق، بيقبل الرفع. مفتاحك السري نفسه معدّاش على الشبكة أبدًا، والرابط بينتهي لوحده بعد المدة اللي حددتها.

الخطوات

  1. جهّز bucket ومستخدم IAM بصلاحية محدودة. اعمل bucket، واعمل مستخدم أو Role عنده صلاحية s3:PutObject على مسار الرفع بس، مش على الـ bucket كله. النتيجة المتوقعة: عندك AWS_ACCESS_KEY_ID وAWS_SECRET_ACCESS_KEY بصلاحية ضيقة.
  2. ثبّت boto3. شغّل الأمر ده. النتيجة: مكتبة AWS جاهزة في بيئتك.
Bash
pip install boto3
  1. اكتب endpoint يولّد الرابط الموقّع. الكود ده على FastAPI. بيستقبل اسم الملف ونوعه، ويرجّع رابط PUT صالح 5 دقايق.
Python
import uuid
import boto3
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()
s3 = boto3.client("s3", region_name="eu-central-1")
BUCKET = "my-uploads-bucket"

class SignRequest(BaseModel):
    filename: str
    content_type: str

@app.post("/uploads/sign")
def sign_upload(req: SignRequest):
    # مفتاح فريد يمنع الكتابة فوق ملفات المستخدمين ببعض
    key = f"uploads/{uuid.uuid4()}-{req.filename}"

    url = s3.generate_presigned_url(
        ClientMethod="put_object",
        Params={
            "Bucket": BUCKET,
            "Key": key,
            "ContentType": req.content_type,  # نثبّت النوع فما يتغيّرش
        },
        ExpiresIn=300,  # 5 دقايق
    )
    return {"upload_url": url, "key": key}
  1. اضبط CORS على الـ bucket. من غير ده، المتصفح هيرفض الـ PUT المباشر لأنه من دومين تاني. حط الإعداد ده في تبويب Permissions > CORS في الـ bucket. النتيجة: المتصفح مسموح له يعمل PUT من موقعك.
JSON
[
  {
    "AllowedOrigins": ["https://your-site.com"],
    "AllowedMethods": ["PUT"],
    "AllowedHeaders": ["Content-Type"],
    "ExposeHeaders": ["ETag"],
    "MaxAgeSeconds": 3000
  }
]
  1. ارفع من المتصفح على مرحلتين. الأول اطلب الرابط من خادمك، بعدين اعمل PUT للملف مباشرة على S3. لاحظ إن الـ Content-Type لازم يطابق اللي وقّعت بيه بالظبط.
JavaScript
async function uploadFile(file) {
  // 1) اطلب رابطًا موقّعًا من خادمك
  const res = await fetch("/uploads/sign", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ filename: file.name, content_type: file.type }),
  });
  const { upload_url, key } = await res.json();

  // 2) ارفع الملف مباشرة على S3 — مش على خادمك
  const put = await fetch(upload_url, {
    method: "PUT",
    headers: { "Content-Type": file.type },
    body: file,
  });

  if (!put.ok) throw new Error("Upload failed: " + put.status);
  return key; // ابعت الـ key لخادمك عشان يسجّله في قاعدة البيانات
}
  1. سجّل الـ key بعد نجاح الرفع. بعد ما الـ PUT يرجّع 200، ابعت الـ key لخادمك ليحفظه في قاعدة البيانات ويربطه بالمستخدم. النتيجة: عندك مرجع للملف من غير ما بايت واحد منه عدّى على خادمك.

التحقق من إنه شغّال

ارفع ملف من الواجهة، وافتح تبويب Network في المتصفح. المفروض تشوف طلبين: طلب صغير لـ /uploads/sign (حجم الرد ~1 كيلوبايت)، وطلب PUT كبير رايح على دومين s3.amazonaws.com مباشرة. لو الملف ظهر في الـ bucket والـ PUT رجّع 200، يبقى تمام. لو رجّع 403 SignatureDoesNotMatch، غالبًا الـ Content-Type في الـ PUT مختلف عن اللي وقّعت بيه.

الأرقام: قبل وبعد

قياس تقريبي على ملف 240 ميجابايت وخادم متوسط:

  • عبر الخادم: ذاكرة محجوزة ~240 ميجابايت لكل رفع متزامن، والاتصال بيقع عند 30 ثانية على البروكسي، والباندويدث بيتحاسب مرتين (دخول للخادم + خروج لـ S3).
  • بالرابط الموقّع: خادمك بيعمل شغل بحجم ~1 كيلوبايت في ~3 مللي ثانية (توليد التوقيع)، الذاكرة المحجوزة للرفع ~صفر، والباندويدث بيتحاسب مرة واحدة على S3 بس.

الافتراض هنا إنك على AWS S3 حقيقي (مش نظام تخزين تاني)، وإن الملفات أقل من 5 جيجابايت (حد الـ PUT الواحد في S3).

الـ trade-offs

بتكسب: خادمك يتفضّى تمامًا من حمل الرفع، ويوسّع مستقل عن حجم الملفات. بتخسر: مبقاش تقدر تفحص أو تعدّل الملف وهو داخل (ضغط صورة، فحص فيروسات) لأنه بيروح S3 مباشرة. الحل إنك تعمل الفحص بعد الرفع عن طريق S3 Event يشغّل Lambda.

نقطة أمان: الرابط الموقّع بيدّي صلاحية كتابة على الـ key ده طول مدة الصلاحية. لو اتسرّب في الـ 5 دقايق دول، حد يقدر يكتب فوق نفس الملف. خفّف الخطر بمدة قصيرة + مفتاح فريد (UUID) + تثبيت ContentType. لو محتاج تفرض حجم أقصى للرفع، استخدم generate_presigned_post بدل put_object، لأنه بيدعم شرط content-length-range.

متى لا تستخدم هذه الطريقة

  • ملفات أكبر من 5 جيجابايت — محتاج multipart upload برابط موقّع لكل جزء.
  • محتاج تتحقق من محتوى الملف أو تحوّله قبل ما يتخزّن أصلاً — هنا الرفع المباشر ميناسبكش.
  • ملفات صغيرة جدًا (بضعة كيلوبايت) وكتير — الطلب الإضافي لجلب الرابط بيزوّد latency من غير مكسب حقيقي.
  • تخزينك مش S3 ولا متوافق معاه (بعض الأنظمة مش بتدعم presign).

الخطوة التالية

افتح أقرب endpoint رفع عندك، وشوف هل الملف بيعدّي على خادمك ولا لأ. لو آه، انقل الرفع لرابط موقّع بنفس الـ 6 خطوات دي، وابدأ بمسار واحد (رفع الصور مثلاً). قِس ذاكرة الخادم قبل وبعد؛ لو نزلت فعلاً، عمّم الطريقة على باقي المسارات.

المصادر

  • AWS — Sharing objects with presigned URLs: docs.aws.amazon.com/AmazonS3/latest/userguide/ShareObjectPreSignedURL.html
  • Boto3 — generate_presigned_url: boto3.amazonaws.com/.../generate_presigned_url.html
  • Boto3 — generate_presigned_post (لفرض شروط الحجم): boto3.amazonaws.com/.../generate_presigned_post.html
  • AWS — CORS configuration on S3: docs.aws.amazon.com/AmazonS3/latest/userguide/ManageCorsUsing.html
  • AWS — Signature Version 4: docs.aws.amazon.com/IAM/latest/UserGuide/reference_sigv.html

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة