مقدمة في تخزين الملفات
يوفر Laravel تجريداً قوياً لنظام الملفات يجعل من السهل العمل مع أنظمة الملفات المحلية، Amazon S3، وخدمات التخزين السحابية الأخرى. تعني واجهة API الموحدة أنه يمكنك تبديل موفري التخزين دون تغيير كود تطبيقك.
ملاحظة: يستخدم Laravel حزمة Flysystem، وهي حزمة PHP قوية، لتوفير واجهة متسقة لأنظمة التخزين المختلفة. يسمح هذا التجريد بالتبديل السلس بين التخزين المحلي والسحابي.
تكوين نظام الملفات
يوجد تكوين التخزين في config/filesystems.php:
<?php
return [
'default' => env('FILESYSTEM_DISK', 'local'),
'disks' => [
'local' => [
'driver' => 'local',
'root' => storage_path('app'),
'throw' => false,
],
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL').'/storage',
'visibility' => 'public',
'throw' => false,
],
's3' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION'),
'bucket' => env('AWS_BUCKET'),
'url' => env('AWS_URL'),
'endpoint' => env('AWS_ENDPOINT'),
],
],
'links' => [
public_path('storage') => storage_path('app/public'),
],
];
فهم الأقراص (Disks)
أقراص تخزين Laravel تمثل مواقع تخزين مختلفة:
- local: يخزن الملفات في
storage/app (خاص، غير متاح عبر الويب)
- public: يخزن الملفات في
storage/app/public (متاح للعامة عبر رابط رمزي)
- s3: يخزن الملفات على Amazon S3 أو خدمات متوافقة
نصيحة: استخدم القرص local للملفات الخاصة (الفواتير، المستندات) والقرص public للملفات العامة (صور المستخدمين، صور المنتجات).
القرص العام والرابط الرمزي
لجعل الملفات في storage/app/public متاحة من الويب، أنشئ رابطاً رمزياً:
# إنشاء رابط رمزي
php artisan storage:link
# هذا ينشئ: public/storage -> storage/app/public
# الآن الملفات المخزنة في storage/app/public يمكن الوصول إليها عبر:
# https://yourapp.com/storage/filename.jpg
تخزين الملفات
استخدم واجهة Storage لتخزين الملفات على أي قرص مُكوَّن:
<?php
use Illuminate\Support\Facades\Storage;
// تخزين ملف على القرص الافتراضي
Storage::put('file.txt', 'المحتويات');
// التخزين على قرص محدد
Storage::disk('public')->put('avatars/user-1.jpg', $fileContents);
// تخزين ملف محمّل
if ($request->hasFile('photo')) {
$path = $request->file('photo')->store('photos');
// يرجع: photos/randomname.jpg
}
// التخزين باسم محدد
$path = $request->file('photo')->storeAs(
'photos',
'user-avatar.jpg'
);
// التخزين على قرص محدد
$path = $request->file('photo')->store('photos', 's3');
// التخزين مع الرؤية
Storage::disk('s3')->put(
'file.jpg',
$contents,
'public' // الرؤية: عامة أو خاصة
);
استرجاع الملفات
استرجع والعمل مع الملفات المخزنة:
<?php
use Illuminate\Support\Facades\Storage;
// فحص وجود الملف
if (Storage::disk('local')->exists('file.txt')) {
// الملف موجود
}
// الحصول على محتويات الملف
$contents = Storage::get('file.txt');
// تنزيل ملف (يرجع استجابة)
return Storage::download('file.pdf');
// التنزيل باسم مخصص
return Storage::download('file.pdf', 'invoice.pdf');
// الحصول على حجم الملف (بالبايت)
$size = Storage::size('file.txt');
// الحصول على آخر وقت تعديل (طابع زمني Unix)
$time = Storage::lastModified('file.txt');
// الحصول على نوع MIME للملف
$mime = Storage::mimeType('file.jpg');
توليد عناوين URL
إنشاء عناوين URL عامة للملفات المخزنة:
<?php
// توليد URL للقرص العام
$url = Storage::disk('public')->url('avatars/user-1.jpg');
// يرجع: http://yourapp.com/storage/avatars/user-1.jpg
// توليد URL مؤقت (S3 فقط، ينتهي بعد 5 دقائق)
$url = Storage::disk('s3')->temporaryUrl(
'file.jpg',
now()->addMinutes(5)
);
// URL مؤقت مع رؤوس مخصصة
$url = Storage::disk('s3')->temporaryUrl(
'file.jpg',
now()->addMinutes(5),
['ResponseContentType' => 'application/pdf']
);
// في عروض Blade
<img src="{{ Storage::url('avatars/user.jpg') }}" alt="صورة رمزية">
// استخدام دالة مساعدة asset (للقرص العام)
<img src="{{ asset('storage/avatars/user.jpg') }}" alt="صورة رمزية">
حذف الملفات
إزالة الملفات من التخزين:
<?php
use Illuminate\Support\Facades\Storage;
// حذف ملف واحد
Storage::delete('file.txt');
// حذف عدة ملفات
Storage::delete(['file1.txt', 'file2.txt']);
// الحذف من قرص محدد
Storage::disk('s3')->delete('folder/file.txt');
// حذف دليل
Storage::deleteDirectory('uploads/temp');
التحقق من رفع الملفات
قم دائماً بالتحقق من الملفات المحملة للأمان والتكامل:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class FileUploadController extends Controller
{
public function upload(Request $request)
{
// التحقق من الملف المحمل
$validated = $request->validate([
'photo' => [
'required',
'file',
'image', // يجب أن يكون صورة (jpg, jpeg, png, bmp, gif, svg, webp)
'mimes:jpeg,png,jpg,gif', // صيغ محددة فقط
'max:2048', // الحجم الأقصى بالكيلوبايت (2MB)
'dimensions:min_width=100,min_height=100,max_width=2000,max_height=2000',
],
'document' => [
'required',
'file',
'mimetypes:application/pdf,application/msword',
'max:5120', // 5MB
],
'video' => [
'required',
'file',
'mimetypes:video/mp4,video/mpeg',
'max:51200', // 50MB
],
]);
// الملف صالح، تابع التخزين
$path = $request->file('photo')->store('uploads');
return response()->json([
'message' => 'تم رفع الملف بنجاح',
'path' => $path,
]);
}
}
تحذير أمني: لا تثق أبداً في الملفات المحملة من المستخدمين. تحقق دائماً من أنواع الملفات والأحجام والمحتويات. يمكن للملفات الضارة أن تعرض خادمك للخطر إذا لم تتم معالجتها بشكل صحيح.
مثال عملي للرفع
مثال كامل مع نموذج، التحقق، والتخزين:
<!-- resources/views/upload.blade.php -->
<form action="{{ route('upload.store') }}" method="POST" enctype="multipart/form-data">
@csrf
<div>
<label for="avatar">صورة الملف الشخصي</label>
<input type="file" name="avatar" id="avatar" accept="image/*">
@error('avatar')
<span class="error">{{ $message }}</span>
@enderror
</div>
<button type="submit">رفع</button>
</form>
<?php
// المتحكم
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
class UploadController extends Controller
{
public function store(Request $request)
{
// التحقق
$request->validate([
'avatar' => 'required|image|max:2048',
]);
// الحصول على الملف
$file = $request->file('avatar');
// توليد اسم ملف فريد
$filename = time() . '_' . $file->getClientOriginalName();
// تخزين الملف
$path = $file->storeAs(
'avatars',
$filename,
'public'
);
// حفظ المسار في قاعدة البيانات
auth()->user()->update([
'avatar' => $path,
]);
return redirect()->back()->with('success', 'تم تحديث الصورة الرمزية!');
}
public function delete(Request $request)
{
$user = auth()->user();
// حذف الصورة الرمزية القديمة
if ($user->avatar) {
Storage::disk('public')->delete($user->avatar);
}
$user->update(['avatar' => null]);
return redirect()->back()->with('success', 'تم حذف الصورة الرمزية!');
}
}
العمل مع الأدلة
إدارة الأدلة وسرد الملفات:
<?php
use Illuminate\Support\Facades\Storage;
// الحصول على جميع الملفات في الدليل
$files = Storage::files('uploads');
// الحصول على جميع الملفات بشكل متكرر
$files = Storage::allFiles('uploads');
// الحصول على الأدلة
$directories = Storage::directories('uploads');
// الحصول على الأدلة بشكل متكرر
$directories = Storage::allDirectories('uploads');
// إنشاء دليل
Storage::makeDirectory('uploads/2024');
// نسخ ملف
Storage::copy('old.jpg', 'new.jpg');
// نقل/إعادة تسمية ملف
Storage::move('old.jpg', 'new.jpg');
// الحصول على مسار الملف (مسار مطلق على القرص المحلي)
$path = Storage::path('file.txt');
استخدام Amazon S3
تكوين واستخدام Amazon S3 لتخزين الملفات:
# تثبيت AWS SDK
composer require league/flysystem-aws-s3-v3 "^3.0"
# تكوين .env
AWS_ACCESS_KEY_ID=your-key-id
AWS_SECRET_ACCESS_KEY=your-secret-key
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=your-bucket-name
AWS_USE_PATH_STYLE_ENDPOINT=false
<?php
// التخزين على S3
Storage::disk('s3')->put('path/file.jpg', $contents);
// التخزين مع رؤية عامة
Storage::disk('s3')->put(
'path/file.jpg',
$contents,
'public'
);
// الحصول على URL عام
$url = Storage::disk('s3')->url('file.jpg');
// توليد URL موقع مؤقتاً (ينتهي خلال 5 دقائق)
$url = Storage::disk('s3')->temporaryUrl(
'file.jpg',
now()->addMinutes(5)
);
// تعيين القرص الافتراضي إلى S3
// في .env: FILESYSTEM_DISK=s3
// الآن Storage::put() يستخدم S3 تلقائياً
Storage::put('file.jpg', $contents);
الأقراص المخصصة
إنشاء أقراص تخزين مخصصة لاحتياجات محددة:
<?php
// config/filesystems.php
'disks' => [
'uploads' => [
'driver' => 'local',
'root' => storage_path('app/uploads'),
'url' => env('APP_URL').'/uploads',
'visibility' => 'public',
],
'backups' => [
'driver' => 'local',
'root' => storage_path('app/backups'),
],
'documents' => [
'driver' => 'local',
'root' => storage_path('app/documents'),
'throw' => false,
],
];
// الاستخدام
Storage::disk('uploads')->put('file.pdf', $contents);
Storage::disk('backups')->put('backup.sql', $sqlDump);
معالجة الصور
استخدم Intervention Image لمعالجة الصور:
# تثبيت Intervention Image
composer require intervention/image
# التكوين في config/app.php
'providers' => [
Intervention\Image\ImageServiceProvider::class,
],
'aliases' => [
'Image' => Intervention\Image\Facades\Image::class,
],
<?php
use Intervention\Image\Facades\Image;
public function upload(Request $request)
{
$request->validate([
'photo' => 'required|image|max:5120',
]);
$file = $request->file('photo');
// تغيير حجم الصورة
$image = Image::make($file)
->resize(800, 600, function ($constraint) {
$constraint->aspectRatio(); // الحفاظ على نسبة العرض إلى الارتفاع
$constraint->upsize(); // منع التكبير
})
->encode('jpg', 85); // تحويل إلى JPG بجودة 85%
// توليد اسم الملف
$filename = uniqid() . '.jpg';
// تخزين الصورة المعدلة
Storage::disk('public')->put(
'photos/' . $filename,
(string) $image
);
// إنشاء صورة مصغرة
$thumbnail = Image::make($file)
->fit(200, 200) // القص إلى حجم محدد
->encode('jpg', 80);
Storage::disk('public')->put(
'photos/thumbs/' . $filename,
(string) $thumbnail
);
return back()->with('success', 'تم رفع الصورة!');
}
تمرين 1: نظام رفع الصورة الرمزية
أنشئ نظام رفع صورة رمزية كامل مع التحقق، التخزين، والعرض.
- أنشئ نموذج رفع مع إدخال ملف (قبول الصور فقط)
- التحقق: مطلوب، نوع صورة، بحد أقصى 2MB، أبعاد دنيا 100x100
- التخزين على القرص العام في مجلد "avatars"
- توليد اسم ملف فريد باستخدام معرف المستخدم والطابع الزمني
- حذف الصورة الرمزية القديمة إن وجدت قبل رفع الجديدة
- تحديث نموذج المستخدم مع مسار الصورة الرمزية
- عرض الصورة الرمزية في ملف المستخدم باستخدام Storage::url()
تمرين 2: رفع ملفات متعددة
أنشئ نظام رفع مستندات يقبل عدة ملفات PDF في وقت واحد.
- أنشئ نموذجاً مع إدخال ملفات متعددة (قبول PDFs فقط)
- تحقق من كل ملف: نوع PDF، بحد أقصى 5MB لكل ملف
- تخزين جميع الملفات في مجلد "documents" بالأسماء الأصلية
- حفظ بيانات وصفية للملفات في جدول documents (الاسم، المسار، الحجم، نوع mime)
- إنشاء مسار تنزيل يبث الملف مع رؤوس صحيحة
- إدراج جميع المستندات المحملة مع روابط التنزيل
تمرين 3: معرض صور مع صور مصغرة
بناء معرض صور ينشئ تلقائياً صور مصغرة للصور المحملة.
- ثبّت حزمة Intervention Image
- أنشئ نموذج رفع يقبل صوراً متعددة
- لكل صورة، أنشئ نسختين:
- الأصلية (بحد أقصى 1920x1080، جودة 90%)
- صورة مصغرة (200x200 مقصوصة، جودة 80%)
- تخزين كلاهما في القرص العام (photos/ و photos/thumbs/)
- حفظ المسارات في قاعدة البيانات مع اسم الملف الأصلي
- عرض شبكة المعرض تعرض الصور المصغرة
- النقر على الصورة المصغرة يفتح الصورة بالحجم الكامل في نافذة منبثقة
الملخص
في هذا الدرس، تعلمت:
- تكوين نظام الملفات في Laravel وأنواع الأقراص
- الفرق بين الأقراص local و public و S3
- إنشاء روابط رمزية للتخزين العام
- تخزين واسترجاع الملفات باستخدام واجهة Storage
- قواعد التحقق من رفع الملفات للأمان
- توليد عناوين URL العامة والمؤقتة
- العمل مع الأدلة وعمليات الملفات
- تكوين واستخدام Amazon S3
- إنشاء أقراص تخزين مخصصة
- معالجة الصور باستخدام Intervention Image
الدرس التالي: سنستكشف Laravel Collections، أداة قوية للعمل مع المصفوفات ومعالجة البيانات.