Introduction
Laravel has a powerful file storage abstraction to interact with many types of filesystems. It supports S3-compatible cloud storage like RCS Object Storage to store static files.
This guide explains how to configure the Laravel file storage system for RCS Object Storage and use it to store files that users upload from a web portal.
Prerequisites
Before you begin, you should:
Install Nginx and PHP
Add the ondrej repositories to get the latest version of Nginx and PHP.
$ sudo add-apt-repository -y ppa:ondrej/php
$ sudo add-apt-repository -y ppa:ondrej/nginx-mainline
$ sudo apt update
Install Nginx.
$ sudo apt install nginx
Install PHP and its extensions.
$ sudo apt install php-{cli,fpm,mysql,gd,soap,mbstring,bcmath,common,xml,curl,imagick,zip}
Install Composer.
$ php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
$ php composer-setup.php
$ php -r "unlink('composer-setup.php');"
$ sudo mv composer.phar /usr/local/bin/composer
Configure PHP
Create a new user for the PHP FastCGI Process Manager (PHP-FPM) pool. This user runs the PHP-FPM process and has the website files ownership. It adds extra security and isolation to your website.
$ sudo adduser website $ sudo usermod -a -G website www-data
Create the website directory.
$ sudo mkdir /var/www/website
Change the directory ownership.
$ sudo chown website:website /var/www/website
Copy the default PHP-FPM pool configuration as a template for the new pool.
$ sudo cp /etc/php/8.1/fpm/pool.d/www.conf /etc/php/8.1/fpm/pool.d/website.conf
Open the pool configuration file.
$ sudo nano /etc/php/8.1/fpm/pool.d/website.conf
Add the following changes.
Change the pool name
[www]
to[website]
.Change the line
user = www-data
touser = website
.Change the line
group = www-data
togroup = website
.Change the line
listen = /run/php/php8.1-fpm.sock
tolisten = /run/php/php8.1-fpm-website.sock
.
Save the file and exit.
Open the
php.ini
file.$ sudo nano /etc/php/8.1/fpm/php.ini
Increase PHP maximum upload file size by changing
upload_max_filesize = 2M
toupload_max_filesize = 10M
andpost_max_size = 8M
topost_max_size = 10M
.Remove the default PHP-FPM pool configuration.
$ sudo rm /etc/php/8.1/fpm/pool.d/www.conf
Restart PHP-FPM.
$ sudo systemctl restart php8.1-fpm
Configure Nginx
Disable the default Nginx configuration.
$ sudo unlink /etc/nginx/sites-enabled/default
Create a new Nginx configuration file.
$ sudo nano /etc/nginx/sites-available/website
Add the following configurations.
server { listen 80; listen [::]:80; server_name example.com; root /var/www/website/public; index index.html index.htm index.php; charset utf-8; client_max_body_size 10m; client_body_timeout 60s; location / { try_files $uri $uri/ /index.php?$query_string; } location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } access_log /var/log/example.com-access.log; error_log /var/log/example.com-error.log error; error_page 404 /index.php; location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.1-fpm-website.sock; fastcgi_buffers 32 32k; fastcgi_buffer_size 32k; } }
Make sure you change the domain name
example.com
to your domain. Save the file and exit.Enable Nginx configuration.
$ sudo ln -s /etc/nginx/sites-available/website /etc/nginx/sites-enabled/
Allow Nginx in the firewall.
$ sudo ufw allow 'Nginx Full'
Restart Nginx.
$ sudo systemctl restart nginx
Install Node.js
Laravel needs Node.js to compile its front-end assets. Install the latest Node.js version using the Snap package manager.
$ sudo snap install core
$ sudo snap refresh core
$ sudo snap install --classic node
Create Object Storage
Log in to RCS customer portal.
Navigate to Products -> Objects.
Add Object Storage and give it a label.
Click your Object Storage and navigate to the Bucket tab.
Create a Bucket and give it a name.
Take note of the Hostname, the Secret Key, the Access Key, and the Bucket Name.
Create Web Portal
For the remaining tasks, you need to run them using the website
user. Change the user by running the following command.
$ sudo su website
Create New Laravel Project
Go to the website directory.
$ cd /var/www/website
Use Composer to create a new Laravel project.
$ composer create-project laravel/laravel .
Configure Laravel Filesystem
Laravel file storage has an S3 driver to interact with S3-compatible cloud storage like RCS Object Storage. It requires the Flysystem S3 package. Install it via the Composer package manager.
$ composer require league/flysystem-aws-s3-v3 "^3.0"
Open the .env
file.
$ nano .env
Change the FILESYSTEM_DISK
value to s3
. It tells Laravel file storage to use the S3 driver.
FILESYSTEM_DISK=s3
Add the RCS Object Storage credentials to the environment variables.
AWS_ENDPOINT=https://sgp1.RCSobjects.com
AWS_ACCESS_KEY_ID=1234567890ABCDEFGH
AWS_SECRET_ACCESS_KEY=ABCDEFGHIJKLMNOPQ
AWS_BUCKET=example
Description of the credentials:
AWS_ENDPOINT
is your RCS Object Storage Hostname.AWS_ACCESS_KEY_ID
is your RCS Object Storage Access Key.AWS_SECRET_ACCESS_KEY
is your RCS Object Storage Secret Key.AWS_BUCKET
is your RCS Object Storage Bucket name.
Add Tailwind CSS
This guide uses Tailwind CSS for the CSS framework. Install it via NPM.
$ npm install -D tailwindcss postcss autoprefixer
Create and open the Tailwind CSS configuration file.
$ npx tailwindcss init -p
$ nano tailwind.config.js
Change the content to:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./resources/**/*.blade.php",
"./resources/**/*.js",
],
theme: {
extend: {},
},
plugins: [],
}
Open the resources/css/app.css
file and add the Tailwind CSS directives.
@tailwind base;
@tailwind components;
@tailwind utilities;
Add JavaScript
Open the resources/js/app.js
file and add the following code:
document.getElementById('fileImage').addEventListener('change',function(){
if( this.files.length > 0 ){
document.getElementById('uploadBtn').removeAttribute('disabled');
}
});
Create Views
Create resources/views/gallery.blade.php
file with the following content:
<html>
<head>
<title>Gallery</title>
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body>
<div class="max-w-7xl m-auto">
<h1 class="text-3xl font-bold text-gray-900 text-center py-8 uppercase">Gallery</h1>
<form action="" method="post" enctype="multipart/form-data" class="flex flex-wrap text-center items-center justify-center p-4 rounded-lg items-center">
@csrf
<label class="block">
<input id="fileImage" type="file" name="fileImage" class="block w-full text-sm text-slate-500 pr-6
file:cursor-pointer
file:mr-4 file:py-2 file:px-4
file:rounded-full file:border-0
file:text-sm file:font-semibold
file:bg-indigo-50 file:text-indigo-700
hover:file:bg-indigo-100
"/>
@if ($errors->has('fileImage'))
<span class="block text-red-700 py-4 text-left">{{ $errors->first('fileImage') }}</span>
@endif
</label>
<button id="uploadBtn" disabled class="rounded border border-transparent bg-indigo-600 px-6 py-2 text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 disabled:opacity-50" type="submit">
Upload Image
</button>
</form>
<div class="grid grid-cols-2 gap-x-4 gap-y-8 sm:grid-cols-3 sm:gap-x-6 lg:grid-cols-4 xl:gap-x-8">
@foreach ($images as $image)
<div>
<img class="rounded" src="">
</div>
@endforeach
</div>
</div>
</body>
</html>
Compile the front-end assets.
$ npm run build
Create Controller
Create a GalleryController
.
$ php artisan make:controller GalleryController
Open the GalleryController
file.
$ nano app/Http/Controllers/GalleryController.php
Load the Storage
facade in the GalleryController
file. It contains a set of functions you need to interact with Object Storage.
use Illuminate\Support\Facades\Storage;
Create the index
action. It shows the upload form and lists all images from the gallery
folder in your Bucket. It uses the files
method from the Storage
facade to get an array of all files in a directory.
public function index()
{
$images = Storage::files('gallery');
return view('gallery', compact('images'));
}
Create the upload
action. It handles when a user uploads their images. It validates the file and then stores it in RCS Object Storage using the putFile
method from the Storage
facade.
public function upload(Request $request)
{
$validated = $request->validate([
'fileImage' => 'required|image',
]);
Storage::putFile('gallery', $validated['fileImage'], 'public');
return redirect('/');
}
The following is the full content of the GalleryController
file:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
class GalleryController extends Controller
{
public function index()
{
$images = Storage::files('gallery');
return view('gallery', compact('images'));
}
public function upload(Request $request)
{
$validated = $request->validate([
'fileImage' => 'required|image',
]);
Storage::putFile('gallery', $validated['fileImage'], 'public');
return redirect('/');
}
}
Defining Routes
Open routes/web.php
.
$ nano routes/web.php
Update it with the following code:
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\GalleryController;
Route::get('/', [GalleryController::class, 'index']);
Route::post('/', [GalleryController::class, 'upload']);
Test Web Portal
Open your domain in a browser.
You should see the upload form.
Upload an image.
Check if the image appears in your RCS Object Storage and the gallery below the upload form.
Secure Web Portal with Let's Encrypt
Let's Encrypt provides a free SSL certificate for your website. To generate the certificate, you need to use the Certbot software tool.
Install Certbot.
$ sudo snap install core; sudo snap refresh core $ sudo snap install --classic certbot $ sudo ln -s /snap/bin/certbot /usr/bin/certbot
Generate the SSL certificate.
$ sudo certbot --nginx
Visit your domain in the browser and confirm it has an HTTPS connection.
Let's Encrypt certificate expires after 90 days. Certbot adds the renewal command to the systemd timer or Cron Job to renew the certificate automatically before it expires. You can verify it with the following commands:
$ systemctl list-timers | grep 'certbot\|ACTIVATES'
$ ls -l /etc/cron.d/certbot
Conclusion
Laravel file storage provides a single interface to interact with different storage systems. It has an S3 driver that you can use to integrate RCS Object Storage in Laravel.