PHP-Laravel Advanced File Storage and Uploading

Soulaimaneyh
5 min readFeb 16, 2024

--

PHP-Laravel File Storage and Uploading
PHP-Laravel File Storage and Uploading

Laravel offers a powerful file system abstraction called “Filesystem,” which provides drivers for various file storage systems like local file systems, Amazon S3, FTP, and more. Additionally, Laravel provides features for handling file uploads, validation, and storage.

Table Of Contents

  • Storing, Validation Upload files
  • PHP-Laravel Filesystem
  • Upload file form preview
  • Image Resolution & Quality

Storing, Validation Upload files

File Uploads:

Employ HTML forms with enctype="multipart/form-data" for file uploads. In Blade views, create a form:

<form action="{{ route('profile-image.store') }}" method="POST" enctype="multipart/form-data">
@csrf
<input type="file" name="profile_image">
<button type="submit">Upload</button>
</form>

In JavaScript:

uploadFile() {
const formData = new FormData();
formData.append('file', this.file);

const headers = {
'Content-Type': 'multipart/form-data'
};

axios.post('/api/upload', formData, { headers })
.then(response => {
// Handle success
})
.catch(error => {
// Handle error
});
},

Controller Logic for File Upload:

Handle file uploads in the controller using the store method:

# StoreProfileImageRequest.php
namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreProfileImageRequest extends FormRequest
{
public function authorize()
{
return true; // Adjust authorization logic if needed
}

public function rules()
{
return [
'profile_image' => 'required|image|mimes:jpeg,png,jpg,gif|max:2048',
];
}

// Optionally, you can customize error messages here
public function messages()
{
return [
'profile_image.required' => 'The profile image is required.',
'profile_image.image' => 'The file must be an image.',
'profile_image.mimes' => 'Supported image formats are jpeg, png, jpg, and gif.',
'profile_image.max' => 'The file size must not exceed 2048 kilobytes.',
];
}
}
# StoreProfileImageController.php
public function upload(StoreProfileImageRequest $request)
{
$path = $this->storeProfileImage($request);

return redirect()->back()->with('status', 'File uploaded successfully');
}

private function storeProfileImage(StoreProfileImageRequest $request)
{
$uploadedFile = $request->file('profile_image');
$storagePath = $this->generateStoragePath($uploadedFile);

return $uploadedFile->store($storagePath);
}

private function generateStoragePath($file)
{
$basePath = 'public/images';
$fileName = $this->generateUniqueFileName($file);

return "{$basePath}/{$fileName}";
}

private function generateUniqueFileName($file)
{
return md5(uniqid() . $file->getClientOriginalName()) . '.' . $file->getClientOriginalExtension();
}
  1. File Validation: Laravel provides validation rules for uploaded files. Customize rules to ensure file type, size, etc.

Remember to run php artisan storage:link to create a symbolic link for easy access to stored files from the public web root.

PHP-Laravel Filesystem

File Storage Configuration:

Laravel’s file storage, managed by the Filesystem abstraction, is configured in config/filesystems.php with options for various providers.

Supported Drivers:

  1. Local: The “local” driver refers to the local file system on your server or machine.
  2. FTP (File Transfer Protocol): The “ftp” driver enables file operations using the FTP protocol for remote file access.
  3. SFTP (Secure File Transfer Protocol): The “sftp” driver is similar to FTP but operates over a secure connection, providing encryption for data transfer.
  4. S3 (Amazon Simple Storage Service):The “s3” driver is designed for working with cloud storage, specifically Amazon S3.

Upload file form preview

<form action="{{ route('profile-image.store') }}" method="POST" enctype="multipart/form-data">
@csrf
<input type="file" name="profile_image" id="profileImageInput">
<button type="submit">Upload</button>
</form>

<div id="imagePreview"></div>

<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
<script>
$(document).ready(function() {
$('#profileImageInput').change(function() {
displayImagePreview(this);
});

function displayImagePreview(input) {
var file = input.files[0];
if (file) {
var reader = new FileReader();
reader.onload = function(e) {
$('#imagePreview').html('<img src="' + e.target.result + '" alt="Profile Image Preview">');
};
reader.readAsDataURL(file);
}
}
});
</script>
  • Added an id attribute to the file input (id="profileImageInput") for easier selection with jQuery.
  • Introduced a div with the id imagePreview where the image preview will be displayed.
  • Utilized jQuery to handle the change event of the file input and display the image preview when a file is selected.

Note: Ensure that you have jQuery included in your project

Image Resolutions & Quality

use Intervention\Image\Facades\Image;
use Illuminate\Support\Facades\Storage;
use App\Http\Requests\StoreProfileImageRequest;

class StoreProfileImageController extends Controller
{
/**
* Upload a profile image.
*
* @param StoreProfileImageRequest $request
* @return string
*/
public function upload(StoreProfileImageRequest $request): string
{
$profileImage = $request->file('profile_image');
$storagePath = $this->generateStoragePath($profileImage);

$path = $this->storeProfileImage($profileImage, $storagePath);

return redirect()->back()->with('status', 'File uploaded successfully');
}

/**
* Store the profile image in different resolutions.
*
* @param mixed $profileImage
* @param string $storagePath
* @return string
*/
private function storeProfileImage($profileImage, $storagePath): string
{
$originalPath = $storagePath . '/original';
$this->storeImageWithResolution($profileImage, $originalPath, 'original');

$doublePath = $storagePath . '/double';
$this->storeImageWithResolution($profileImage, $doublePath, 'double', 2);

$triplePath = $storagePath . '/triple';
$this->storeImageWithResolution($profileImage, $triplePath, 'triple', 3);

return $originalPath;
}

/**
* Store an image with a specific resolution.
*
* @param mixed $image
* @param string $path
* @param string $resolutionName
* @param int $multiplier
* @return void
*/
private function storeImageWithResolution($image, $path, $resolutionName, $multiplier = 1): void
{
$fileName = $this->generateUniqueFileName($image, $resolutionName);
$resizedImage = Image::make($image)->resize($multiplier * 300, $multiplier * 300)->encode();

Storage::put("public/images/{$path}/{$fileName}", $resizedImage);
}

/**
* Generate the storage path for the profile image.
*
* @param mixed $file
* @return string
*/
private function generateStoragePath($file): string
{
$basePath = 'images';
$fileName = $this->generateUniqueFileName($file, 'original');

return "{$basePath}/{$fileName}";
}

/**
* Generate a unique file name based on the original file name and resolution.
*
* @param mixed $file
* @param string $resolutionName
* @return string
*/
private function generateUniqueFileName($file, $resolutionName): string
{
return md5(uniqid() . $file->getClientOriginalName() . $resolutionName) . "_{$resolutionName}x." . $file->getClientOriginalExtension();
}
}
use Intervention\Image\Facades\Image;
use Illuminate\Support\Facades\Storage;

/**
* Store an image with a specific resolution.
*
* @param mixed $image
* @param string $path
* @param string $resolutionName
* @param int $multiplier
* @return void
*/
private function storeImageWithResolution($image, $path, $resolutionName, $multiplier = 1): void
{
// Generate a unique file name based on the original file name and resolution
$fileName = $this->generateUniqueFileName($image, $resolutionName);

// Determine the image quality based on the resolution
$quality = $this->calculateImageQuality($multiplier);

// Resize the image with the specified multiplier and set the quality
$resizedImage = Image::make($image)
->resize($multiplier * 300, $multiplier * 300)
->encode(null, null, function ($encode) use ($quality) {
$encode->quality($quality);
});

// Save the resized image to storage
Storage::put("public/images/{$path}/{$fileName}", $resizedImage);
}

/**
* Calculate image quality based on the resolution multiplier.
*
* @param int $multiplier
* @return int
*/
private function calculateImageQuality($multiplier): int
{
// Define quality levels based on resolution
$qualityLevels = [
1 => 90, // Standard resolution
2 => 80, // Double resolution
3 => 70, // Triple resolution
];

// Get the quality for the given multiplier, default to 90 if not found
return $qualityLevels[$multiplier] ?? 90;
}

In this version:

  • The calculateImageQuality method is introduced to dynamically determine the image quality based on the resolution multiplier.
  • The storeImageWithResolution method now calls calculateImageQuality to get the appropriate quality level based on the resolution. Adjust the $qualityLevels array according to your preferences for different resolutions.

Additional Resources

--

--