تخزين الملفات (File storage) في Laravel

من موسوعة حسوب

مقدمة

يوفّر Laravel تجريدًا قويًا لنظام الملفات بفضل الحزمة Flysystem. يوفّر تضمين أنظمة الملفات في Laravel مشغّلات سهلة الاستعمال للتعامل مع الأنظمة المحلية و Amazon S3 والتخزين السحابي Rackspace، بل من السهل جدًا تغيير خيارات التخزين إذ تبقى وصلة API نفسها مع كل الأنظمة.

الضبط

يوجد ملف ضبط نظام الملفات في config/filesystems.php. يمكنك في هذا الملف ضبط كل الأقراص "disks". يمثّل كل قرص مشغّل تخزين ومكان تخزين خاص. يحتوي الملف على أمثلة ضبط لكل مشغّل مدعوم. لذا غيّر الملف حسب خياراتك ومعلوماتك الخاصة.

طبعًا، يمكنك ضبط أي عدد من الأقراص تريد أو حتى أقراص عديدة تستعمل نفس المشغّل.

القرص الصلب العام

القرص العام public مُعدٌّ للملفات التي سيكون ولوجها عامًّا. في العادة، يستخدم القرص public المشغل local ويُخزِّن الملفات في storage/app/public. لإتاحة إمكانية ولوجهم من الويب، يجب إنشاء وصلة رمزية من public/storage إلى storage/app/public. تُبقي هذه الطريقة كل الملفات العمومية في مكان واحد يمكن مشاركته بسهولة عند استعمال تقنية نشر دون وقت إيقاف مثل Envoyer.

لإنشاء الوصلة الرمزية، استعمل الأمر storage:link:

php artisan storage:link

بعد إنشاء الملف والوصلة الرمزية، يمكنك توليد عنوان URL للملف عبر الدالة المساعدة asset:

echo asset('storage/file.txt');

برنامج التشغيل المحلي

عند استخدام المشغّل المحلي local، كل الأعمال على الملفات موجودة في المجلد الجذر (root) المعرّف في ملف الضبط. في العادة، قيمته هي المجلد storage/app. لذا، الأمر الآتي سيُسجّل ملفًا في storage/app/file.txt:

Storage::disk('local')->put('file.txt', 'Contents');

متطلبات برنامج التشغيل

حزم Composer

قبل استخدام مشغّلات S3 أو SFTP أو Rackspace، ستحتاج أولا لتثبيت الحزم الملائمة عبر Composer:

  • league/flysystem-sftp ~1.0 :SFTP
  • league/flysystem-aws-s3-v3 ~1.0 :Amazon S3
  • league/flysystem-rackspace ~1.0 :Rackspace

من الضروري لأداء جيد استخدام مكيّفات تخزين مؤقت (cached adapter). ستحتاج حزمة أخرى لذلك:

  • league/flysystem-cached-adapter ~1.0 : CachedAdapter

ضبط مشغّل S3

توجد معلومات ضبط المشغّل S3 في الملف config/filesystems.php. يحتوي هذا الملف على مثال لمصفوفة ضبط لمشغل S3. لك حرية تغيير هذه المصفوفة بضبتك ومعلوماتك الخاصة. للتسهيل، يستعمل متغيرات البيئة أسماء مطابقة لطريقة التسمية  المتبعة في واجهة سطر الأوامر لخدمات AWS.

ضبط مشغّل FTP

يعمل مضمِّن نظام الملفات جيدًا مع FTP. لكن الملف filesystems.php لا يتضمن مثالًا عن الضبط. إن احتجت لضبط FTP يمكنك استعمال المثال التالي:

'ftp' => [

   'driver'   => 'ftp',
   'host'     => 'ftp.example.com',
   'username' => 'your-username',
   'password' => 'your-password',

  // خيارات غير إجبارية

  // 'port'     => 21,
  // 'root'     => '',
  // 'passive'  => true,
  // 'ssl'      => true,
  // 'timeout'  => 30,

],

ضبط مشغّل SFTP

يعمل مضمِّن نظام الملفات جيدًا مع SFTP. لكن الملف filesystems.php لا يتضمن مثالًا عن الضبط. إن احتجت لضبط FTP يمكنك استعمال المثال التالي:

'sftp' => [

   'driver' => 'sftp',
   'host' => 'example.com',
   'username' => 'your-username',
   'password' => 'your-password',

  // ضبط مفتاح SSH
  // 'privateKey' => '/path/to/privateKey',
  // 'password' => 'encryption-password',
  // Optional SFTP Settings...
  // 'port' => 22,
  // 'root' => '',
  // 'timeout' => 30,

],

ضبط مشغّل Rackspace

يعمل مضمِّن نظام الملفات جيدًا مع Rackspace. لكن الملف filesystems.php لا يتضمن مثالًا عن الضبط. إن احتجت لضبط FTP يمكنك استعمال المثال التالي:

'rackspace' => [

   'driver'    => 'rackspace',
   'username'  => 'your-username',
   'key'       => 'your-key',
   'container' => 'your-container',
   'endpoint'  => 'https://identity.api.rackspacecloud.com/v2.0/',
   'region'    => 'IAD',
   'url_type'  => 'publicURL',

],

التخزين المؤقت

لتمكين قرص ما من استخدام التخزين المؤقت، تضاف التعليمة cache لخيارات ضبط القرص. يجب أن يكون الخيار cache مصفوفة من خيارات التخزين تحتوي الاسم disk

's3' => [

   'driver' => 's3',
  // خيارات أخرى

   'cache' => [

       'store' => 'memcached',
       'expire' => 600,
       'prefix' => 'cache-prefix',

   ],
],

وتاريخ انتهاء الصلاحية expire إضافة إلى سابقة prefix:

الحصول على نسخة من كائن القرص

تُستعمل الواجهة الساكنة Storage للتفاعل مع عدّة أقراص مضبوطة. مثلًا، يٌستعمل التابع put من الواجهة لتخزين نسخة avatar في القرص الرئيسي. إذا ناديت التابع put على الواجهة الثابتة Storage دون أن تنادي call أولا،تُستدعى الدالة call تلقائيًا للقرص الرئيسي:

use Illuminate\Support\Facades\Storage;
Storage::put('avatars/1', $fileContents);

إذا كان التطبيق يتعامل مع أكثر من قرص، يمكن استعمال التابع disk من الواجهة الثابتة Storage

Storage::disk('s3')->put('avatars/1', $fileContents);

للعمل على ملفات في قرص معيّن:

Storage::disk('s3')->put('avatars/1', $fileContents);

استرداد الملفات

يُستعمل التابع get للحصول على محتوى ملف. يعيد التابع سلسلة نصية خام من محتويات الملف. تذكر أنّ كل مسارات الملفات يجب أن تُذكر في علاقة بالملف الجذر "root" المحدّد في ملف الضبط:

$contents = Storage::get('file.jpg');

يُستعمل التابع exists للتأكد من وجود الملف على القرص:

$exists = Storage::disk('s3')->exists('file.jpg');

تنزيل الملفات

يُستعمل التابع download لتوليد إجابة تفرض على متصفح المستخدم تنزيل ملف من مسار ممرَّر. يقبل التابع download اسم ملف كمعامل ثاني يُحدِّد اسم الملف الذي سيراه المستخدم بعد تنزيل الملف. أخيرًا، يمكن تمرير مصفوفة من ترويسات HTTP كمعامل ثالث للتابع

return Storage::download('file.jpg');
return Storage::download('file.jpg', $name, $headers);

عنوان URL الملفات

يمكن استعمال التابع url للحصول على مسار URL لملف معيّن. إن كنت تستعمل المشغّل local، سيضيف التابع القيمة storage/ للعنوان الممرَّر ويعيد النتيجة. إن كنت تستخدم المشغّل s3 أو rackspace، سيعيد التابع العنوان البعيد الكامل:

use Illuminate\Support\Facades\Storage;
$url = Storage::url('file.jpg');

تنبيه: تذكر أنّه إن كنت تستعمل المشغّل local، يجب أن توجد جميع الملفات العامة في الدليل storage/app/public. أيضًا، عليك إنشاء وصلة رمزية من public/storage إلى storage/app/public.

عناوين URL المؤقتة

بالنسبة للملفات المخزَّنة باستعمال مشغّلات S3 أو rackspace، يمكن إحداث عنوان URL مؤقت لملف معيّن باستعمال التابع temporaryUrl. يقبل هذا التابع مسارًا ونسخة الكائن DateTime يحدّد وقت انتهاء صلاحية المسار:

$url = Storage::temporaryUrl(

   'file.jpg', now()->addMinutes(5)
);

تخصيص عناوين URL المحلية

إذا أردت التعريف المسبق للمستضيف للملفات المخزّنة على قرص باستعمال المشغّل local، يمكن إضافة الخيار url لمصفوفة ضبط القرص:

'public' => [

   'driver' => 'local',
   'root' => storage_path('app/public'),
   'url' => env('APP_URL').'/storage',
   'visibility' => 'public',

],

وصف بيانات الملفات File Metadata

بالإضافة لقراءة وكتابة الملفات، يوفّر Laravel معلومات عن الملفات نفسها. مثلًا، يُستعمل التابع size لإعادة معلومات عن حجم الملف بالوحدة byte:

use Illuminate\Support\Facades\Storage;
$size = Storage::size('file.jpg');

يعيد التابع lastModified وقت آخر تغيير طرأ على الملف:

$time = Storage::lastModified('file.jpg');

تخزين الملفات

يُستعمل التابع put لتخزين محتويات خام لملف على قرص. يمكن تمرير مورد (php resource) للتابع put، ممّا يسمح باستغلال خاصية دعم التدفق في Flysystem. يُنصح  باستخدام التدفق في حال العمل بملفات ضخمة:

use Illuminate\Support\Facades\Storage;

Storage::put('file.jpg', $contents);
Storage::put('file.jpg', $resource);

التدفّق التلقائي

إذا أردت أن يتحكم Laravel تلقائيا بتدفق ملف معيّن في مكان التخزين، استعمل التابع putFile أو putFileAs.

يقبل هذا التابع نسخة عن Illuminate\Http\File أو lluminate\Http\UploadedFile ويقوم بدفق الملف للمكان المحدّد:

use Illuminate\Http\File;
use Illuminate\Support\Facades\Storage;
// إحداث رقم خاص بالملف تلقائيًا
Storage::putFile('photos', new File('/path/to/photo'));

// اختيار اسم الملف يدويًا
Storage::putFileAs('photos', new File('/path/to/photo'), 'photo.jpg');

هناك بعض المعلومات يجب معرفتها عند العمل بالتابع putFile. لاحظ أننا حددنا فقط اسم المجلد وليس اسم الملف. في العادة يصنع التابع رقم هوية ID فريدا لتكون اسم الملف. يُحدَّد ملحق اسم الملف بمعاينة نوع MIME الملف. يعيد التابع مسار الملف لذا يمكنك تخزين المسار بما فيه الإسم المصنوع في قاعدة البيانات. يقبل التابعان putFile و putFileAs أيضًا معاملًا لتحديد إمكانية رؤية الملف المخزَّن. هذه الخاصية مفيدة خاصة في حال تخزين ملف في قرص سحابي مثل s3 وجعل الوصول إليه عموميًا:

Storage::putFile('photos', new File('/path/to/photo'), 'public');

الإضافة للملف

يسمح التابعان prepend و append بالإضافة لملفٍ ما في بدايته أو في نهايته:

Storage::prepend('file.log', 'إضافة في البداية');
Storage::append('file.log', 'إضافة في النهاية');

نسخ ونقل الملفات

يُستعمل التابع copy لنقل نسخة من ملف موجود لمكان جديد في القرص، في حين يُستخدم move لإعادة تسمية ملف أو نقله كليًا لمكان جديد:

Storage::copy('old/file.jpg', 'new/file.jpg');
Storage::move('old/file.jpg', 'new/file.jpg');

رفع الملفات

في تطبيقات الويب، يعدّ رفع ملف مثل صورة أو وثيقة من حالات الاستخدام الشائعة جدًا في تخزين الملفات. يجعل Laravel تخزين الملفات المرفوعة أمرًا سهلًا باستخدام التابع store على نسخة الكائن من الملف المرفوع. استدعِ التابع store مع المسار الذي تريد أن تخزّن الملف المرفوع فيه:

<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class UserAvatarController extends Controller
{

   /**
    * تحديث صورة المستخدم
    *
    * @param  Request $request
    * @return Response
    */

   public function update(Request $request)
   {

       $path = $request->file('avatar')->store('avatars');
       return $path;

   }
}

هناك بعض الأمور المهمة التي يجب معرفتها. مثلًا، لاحظ أننا حددنا فقط اسم المجلد وليس اسم الملف. في العادة يُنشِئ التابع رقم مُعرِّف ID فريد ليكون اسم الملف. يُحدَّد ملحق اسم الملف بمعاينة نوع MIME للملف. يعيد التابع مسار الملف لذا يمكنك تخزين المسار بما فيه الاسم المُنشَأ في قاعدة البيانات.

يمكنك أيضًا استدعاء putFile من الواجهة الثابتة Storage للقيام بنفس العمل في المثال السابق:

$path = Storage::putFile('avatars', $request->file('avatar'));

تحديد اسم الملف

إذا لم ترد أن يعطى اسم آلي للملفات، استعمل التابع storeAs الذي يقبل مسارًا، واسمًا للملف، وقرصًا (غير إجباري) كمعاملات:

$path = $request->file('avatar')->storeAs(
  'avatars', $request->user()->id

);

يمكنك أيضًا استعمال التابع putFileAs من الواجهة الثابتة Storage للقيام بنفس عمل المثال السابق:

$path = Storage::putFileAs(
   'avatars', $request->file('avatar'), $request->user()->id

);

تحديد القرص

في العادة، يستخدم هذا التابع القرص الرئيسي. إذا أردت تحديد قرص آخر، مرّر اسم القرص كمعامل ثاني للتابع store:

$path = $request->file('avatar')->store(
   'avatars/'.$request->user()->id, 's3'

);

إمكانية رؤية الملفات

في تضمين Flysystem من Laravel، إمكانية رؤية الملفات (visibility) هي تجريد لأذونات الملفات في مختلف المنصات. يمكن تعريف الملفات كملفات عمومية (public) أو خاصة (private). عند تعريف ملف كعمومي، أنت تعلن أنّ يمكن رؤيته من الآخرين. مثلًا، عند استخدام برنامج التشغيل S3، يمكنك استرداد عناوين URLs الخاصة بالملفات العامّة (public files).

يمكن تحديد إمكانية الرؤية عند إنشاء الملف عبر التابع put:

use Illuminate\Support\Facades\Storage;
Storage::put('file.jpg', $contents, 'public');

إذا كان الملف موجودًا مسبقًا، يمكن إعادة إمكانية الرؤية وتغييرها باستخدام التابعين getVisibility و setVisibility:

$visibility = Storage::getVisibility('file.jpg');
Storage::setVisibility('file.jpg', 'public')

حذف الملفات

يقبل التابع delete اسم ملف واحد أو مصفوفة ملفات لحذفها من القرص:

use Illuminate\Support\Facades\Storage;

Storage::delete('file.jpg');
Storage::delete(['file.jpg', 'file2.jpg']);

يمكن تحديد القرص الذي يحتوي الملف إن احتجت لذلك:

use Illuminate\Support\Facades\Storage;
Storage::disk('s3')->delete('folder_path/file_name.jpg');

المجلدات

إعادة كل الملفات من مجلد

يُرجع التابع files كل الملفات الموجودة في مجلّد ممرَّر كوسيط إليه. إذا أردت استرجاع كل الملفات حتى من المجلدات الفرعية، استخدم التابع allFiles:

use Illuminate\Support\Facades\Storage;

$files = Storage::files($directory);
$files = Storage::allFiles($directory);

إعادة كل المجلدات من مجلد معين

يُرجع التابع directories كل المجلدات الموجودة  في مجلد ممرّر كوسيط إليه . إذا أردت استرجاع كل المجلدات حتى من المجلدات الفرعية، استخدم التابع allDirectories:

$directories = Storage::directories($directory);

// Recursive...
$directories = Storage::allDirectories($directory);

إنشاء مجلد

يُستخدم التابع makeDirectory لصناعة مجلد ممرَّر كوسيط إليه بما فيه المجلدات الفرعية

Storage::makeDirectory($directory);

حذف مجلد

يُستخدم التابع deleteDirectory لحذف مجلد وكل ملفاته:

Storage::deleteDirectory($directory);

نظام الملفات المخصص

وفّر تضمين Flysystem في Laravel واجهة لعدّة برامج تشغيل. لكنّه ليس محدودًا بها بل يوفّر أيضًا مكيّفات لعدّة أنظمة تخزين أخرى. يمكنك إنشاء برنامج تشغيل مخصص إذا أردت استعمال أحد المكيّفات الإضافية في تطبيقك.

لوضع نظام ملفات مخصص، ستحتاج لمكيّف Flysystem. لنضف المكيّف Dropbox مدعوما من مجموعة مستخدمين:

composer require spatie/flysystem-dropbox

بعد ذلك، يجب إنشاء موّفر خدمات مثل DropboxServiceProvider. في التابع boot من الموفر، استعمل التابع extend الواجهة الثابتة Storage لتعريف المشغّل المخصص:

<?php

namespace App\Providers;

use Storage;
use League\Flysystem\Filesystem;
use Illuminate\Support\ServiceProvider;
use Spatie\Dropbox\Client as DropboxClient;
use Spatie\FlysystemDropbox\DropboxAdapter;

class DropboxServiceProvider extends ServiceProvider
{

   /**
    * تنفيذ خدمات ما قبل التسجيل 
    * @return void
    */

   public function boot()
   {

       Storage::extend('dropbox', function ($app, $config) {

           $client = new DropboxClient(
               $config['authorization_token']
          );

          return new Filesystem(new DropboxAdapter($client));

       });
   }

   /**
    * تسجيل الارتباطات  في الحاوي
    *
    * @return void
    */

   public function register()

   {
      //
   }
}

المعامل الأول في التابع extend هو اسم المشغّل، المعامل الثاني Closure يقبل المتغيّرين app$ و  config$. يعيد Closure بعد انتهائه نسخةً من League\Flysystem\Filesystem. يحتوي المتغيّر config$ قيمة config/filesystems.php للقرص المذكور.

بعد إنشاء موّفر الخدمات، يمكن استعمال برنامج التشغيل dropbox في ملف الضبط config/filesystems.php.

مصادر