How to Upload File to Amazon S3 in Laravel in the best way?

We can upload a file directly to S2 using Laravel Filesystem. We have learnt of basic ways to upload files and images using the public path and storage path locations. In this tutorial, we will see a step by step way on how to upload file in Amazon S3 server and in S3 storage location using Laravel. We can use this example from Laravel 6 onwards.

Step 1: Create a S3 Bucket

  • You should create the S3 bucket. Log in to your Amazon console for this and select S3 from the list of services.
  • Next, click ‘Create Bucket’ option. Here you can enter bucket name and other required details.
  • Secondly, you will have to create an Identity and Access Management (IAM) user profile. Then set the user details, like username and select “Programmatic access” from access type.
  • Now you will select “Attach Existing Policy Directly” and choose “AmazonS3FullAccess” from permission link.
  • You can view all user details and then click on “Create User” button.
  • Now as the user profile is created, you will see created user in link. Here you can find an “Access Key ID” and “Secret Access Key” that will be required in .env files.

Step 2: Install Laravel and create new Project

Create the Laravel Project.

Now comes the Laravel application part and required installations.

composer create-project --prefer-dist laravel/laravel-aws

Install Amazon S3 Composer package.

composer require --with-all-dependencies league/flysystem-aws-s3-v3 "^1.0"

Configure S3 credentials.

You will need to enter your Amazon credentials in the .env (environment variables) file.

AWS_ACCESS_KEY_ID=AKIDVAEWWHTDY...
AWS_SECRET_ACCESS_KEY=Zp/wgej46HAC....
AWS_DEFAULT_REGION=us-east-2
AWS_BUCKET=scratchcoding-bucket
AWS_USE_PATH_STYLE_ENDPOINT=false

Step 3: Create new Routes

Add the required routes to the web.php file.

Route::get('image-file-upload', [ ImageFileUploadController::class, 'imageFileUpload' ])->name('image_file.upload');
Route::post('image-file-upload', [ ImageFileUploadController::class, 'imageFileUploadPost' ])->name('image_file.upload.post');

Step 4: Create new Controller Class

The controller class handles all methods for file storage as below.

<?php
  
namespace App\Http\Controllers;
  
use Illuminate\Http\Request;
  
class ImageFileUploadController extends Controller
{
     /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function imageFileUpload()
    {
        return view('imageFileUpload');
    }
    
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function imageFileUploadPost(Request $request)
    {
        $request->validate([
            'image' => 'required|image|mimes:jpeg,png,jpg,gif,svg|max:2048',
        ]);
    
        $imageName = time().'.'.$request->image->extension();  
     
        $path = Storage::disk('s3')->put('images', $request->image);
        $path = Storage::disk('s3')->url($path);
  
        /* Store $imageName name in DATABASE from HERE */
    
        return back()
            ->with('success','You have successfully upload image.')
            ->with('image', $path); 
    }
}

It does the same steps as we do for normal image file uploads to the public path. However, here you need to store (put) image files to the ‘s3’ disk location, in the Storage façade.

Step 5: Create new blade file

Now we will create a blade view file that prompts a user the option to upload a file or image.

<!DOCTYPE html>
<html>
<head>
    <title>Laravel File Uploads with AWS S3 - scratchcoding.dev</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
</head>
    
<body>
<div class="container">
     
    <div class="panel panel-primary">
      <div class="panel-heading"><h2>Laravel File Uploads with AWS S3 - scratchcoding.dev</h2></div>
      <div class="panel-body">
     
        @if ($message = Session::get('success'))
        <div class="alert alert-success alert-block">
            <button type="button" class="close" data-dismiss="alert">×</button>
                <strong>{{ $message }}</strong>
        </div>
        <img src="{{ Session::get('image') }}">
        @endif
    
        @if (count($errors) > 0)
            <div class="alert alert-danger">
                <strong>Whoops!</strong> There were some problems with your input.
                <ul>
                    @foreach ($errors->all() as $error)
                        <li>{{ $error }}</li>
                    @endforeach
                </ul>
            </div>
        @endif
    
        <form action="{{ route('image_file.upload.post') }}" method="POST" enctype="multipart/form-data">
            @csrf
            <div class="row">
    
                <div class="col-md-6">
                    <input type="file" name="image" class="form-control">
                </div>
     
                <div class="col-md-6">
                    <button type="submit" class="btn btn-success">Upload</button>
                </div>
     
            </div>
        </form>
    
      </div>
    </div>
</div>
</body>

The blade file above views the upload file option to the user. In addition, it has two separate divs that pop up required messages. One for success and one for error. The error directive will display any error messages.