File Store বা Upload করা যেকোন Application এর জন্যই একটি গুরুত্বপূর্ণ অংশ। Laravel এ File handling এর জন্য File Storage নামে একটি feature রয়েছে, যেটি ব্যবহার করে খুব সহযেই File Store, Copy, Move ইত্যাদি করা যায়।
শুধু তাই নয় এই File Storage feature ব্যবহার করে আপনি Amazon S3, Rackspace বা FTP তে সহযেই File upload বা delete করতে পারবেন।

Laravel File Storage: https://laravel.com/docs/5.5/filesystem

আমি এখানে Laravel File Storage কে আমরা নিজের ভাষায় ছোট লিখার চেষ্টা করবো, আপনি যদি এটি সম্বন্ধে বিস্তারিত জানতে চান তাহলে অবশ্যই Laravel এর official documentation টি পড়ুন।

Storage disk

Disk বলতে বুঝানো হচ্ছে যেখানে আপনার file গুলু থাকবে, যেমনঃ Public, Local, S3, Rackspace ইত্যাদি।

Filesystem configuration

config/filesystems.php এই file থেকে আপনার disk এর configuration add বা edit করতে পারবেন।
এই file মূলত একটি Array return করে, সেখানে আপনি প্রথমেই দেখতে পাবেন default হিসেবে local set করা আছে। যার মানে হচ্ছে আপনি ফাইল manipulate করার সময় কোন disk উল্লেখ না করলে সেটি Local disk কে ব্যবহার করবে। আপনি চাইলে এটিকে পরিবর্তন করতে পারেন।
একটু নিচে নামলে disks নামে একটি key পাবেন যেখানে কিছু disk এর information দেয়া আছে আপনি চাইলে আরও যুক্ত করতে পারেন।
সেখানে public নামে একটি disk সেট করা আছে যেটি আপনাকে publicly access করার জন্য একটি folder দিবে।

Difference between public and local disk

Local: এই disk এর location হচ্ছে storage/app এর ভিতরের ফাইল গুলিকে আপনি PHP থেকে access করতে পারবেন কিন্তু URL থেকে access করতে পারবেন না।

Public: এই disk এর location হচ্ছে storage/app/public এর ভিতরের ফাইল গুলিকে আপনি PHP থেকে access করতে পারবেন আবার URL থকেও access করতে পারবেন।

How to access public disk publicly?

এই কাজটি করার জন্য কষ্ট করে এই command টি run করুন।

php artisan storage:link

এখন আপনি public disk এ কোন ফাইল রাখলে সেটিকে http://example.com/storage/file.txt থেকে access করতে পারবেন।

How to put file inside public disk?

প্রথমে আপনার view ফাইলে একটি ফর্ম বনান তারপর সেটি থেকে আপনার controller এ post request পাঠান। তাহলে চলুন একটি form তৈরি করা যাক।

<form method="post" action="/media/upload" enctype="multipart/form-data">
	{{ csrf_field() }}
	
	<div class="form-group">
		<label>Name</label>
		<input type="text" class="form-control" placeholder="Enter name" name="name">
	</div>
	<div class="form-group">
		<label>Email address</label>
		<input type="email" class="form-control" placeholder="Enter email" name="email">
	</div>
	<div class="form-group">
		<label>File input</label>
		<input type="file" class="form-control-file" name="image">
	</div>

	<button type="submit" class="btn btn-primary">Submit</button>
</form>

এখন Controller এ কিছু কোড লিখি।

public function upload(Request $request)
{
	$validateData = $request->validate([
		'name' => 'required|min:2',
		'email' => 'required|email',
		'image' => 'required|mimes:jpeg,png,gif'
	]);

	// Move uploaded file
	$path = $request->file('image')->store('images', 'public');

	// Get new file url
	$url = Storage::disk('public')->url($path);

	// Save to database
	$media = Media::create([
		'name' => $validateData['name'],
		'email' => $validateData['email'],
		'image' => $url
	]);

	return redirect()->route('media.show', compact('media'));
}

এখানে আমরা প্রথমে field গুলোকে validate করেছি, তারপর upload করা file টিকে আমাদের public disk এ store করেছি, তারপর সেই file টির URL নিয়েছি এবং সব শেষে সবগুলো field এর data এবং Image এর URL database এ store করেছি।

এখানে একটি problem আছে, তা হচ্ছে ফাইলটির নাম Laravel নিজের ইচ্ছেমত চল্লিশ আক্ষরের একটি string generate করে দিয়েছে। তবে আমরা চাইলে ফাইলের নামটির সাথে মিল রেখে রাখতে পারি, এখানে challenge হচ্ছে একই নাম দ্বিতীয় বার আসলে আগের ফাইলটি replace হয়ে যাবে।

Set unique filename similar to uploaded filename

এর জন্য আমাদের upload মেথডটিতে কিছু পরিবর্তন করতে হবে এবং নতুন unique filename generate করার জন্য নতুন একটি method তৈরি করতে হবে।
পরিবর্তন করার পর upload method টি হবে এই রকম।

public function upload(Request $request)
{
    $validateData = $request->validate([
        'name' => 'required|min:2',
        'email' => 'required|email',
        'image' => 'required|mimes:jpeg,png,gif'
    ]);

    $theFile = $request->file('image');
    $folder = 'images';
    $disk = Storage::disk('public');

    $uniqueFilename = $this->uniqueMediaName($theFile, $folder, $disk);

    // Move uploaded file
    $path = $theFile->storeAs($folder, $uniqueFilename, 'public');

    // Get new file url
    $url = $disk->url($path);

    // Save to database
    $media = Media::create([
        'name' => $validateData['name'],
        'email' => $validateData['email'],
        'image' => $url
    ]);

    return redirect()->route('media.show', compact('media'));
}

এবং আমাদের নতুন method টি হবে নিচের মত, যেটি একটি folder এ থাকা ফাইল গুলো থেকে unique নাম তৈরি করে দিবে।
যদি উদাহরণ দিয়ে বলতে চাই তাহলে ধরুন আপনি filename.png ফাইলটি upload দিয়েছেন, এখন ঐ folder এ যদি এই নামের অন্য আর কোন ফাইল না থাকে তাহলে তাহলে এটি return করবে filename.png এবং যদি আগে থেকেই থাকে তাহলে return করবে filename-1.png.

public function uniqueMediaName($theFile, $folder, $disk)
{
    $filename = $theFile->getClientOriginalName();
    $extension = $theFile->getClientOriginalExtension();

    $i = 1;
    $basename = basename($filename, '.' . $extension);
    $output = $folder.'/'.$filename;
    while($disk->exists($output)){
        $output = $folder .'/'. $basename .'-'. $i . '.' . $extension;
        $i++;
    }
    return basename($output);
}

আজ এই পর্যন্তই থাক, পরে কখনও আরও advance topic নিয়ে লিখার চেষ্টা করবো, কষ্ট করে পড়ার জন্য ধন্যবাদ।

বিস্তারিত জানতে অবশ্যই নিচের লিঙ্ক দুটি দেখবেন।
https://laravel.com/docs/5.5/filesystem
http://api.symfony.com/3.0/Symfony/Component/HttpFoundation/File/UploadedFile.html

কোন ভুল হয়ে থাকলে অবশ্যই ধরিয়ে দিবেন।