DEV Community

dami
dami

Posted on • Updated on

Building a Simple File Upload API with Laravel 9 and AWS S3

Uploading and storing files securely and efficiently is essential for many modern web applications. Whether it's uploading images, documents, or other types of files, having a reliable file upload system is crucial for the success of any online business or application.

This article will explore how to build a simple file upload API using Laravel 9 and Amazon Web Services (AWS) S3. Laravel is a popular PHP web framework that provides a rich set of features for building web applications. AWS S3 is a cloud storage service that offers high scalability and reliability, making it an ideal solution for storing and retrieving files of any size.

We will explore the process of setting up a new Laravel project, creating an API endpoint for file uploads, and configuring AWS S3 to store and retrieve the files. By the end of this article, readers will have a fully functional file upload API that they can integrate into their web application or use as a standalone service. So, let's get started!

Prerequisites

Before diving into building this API, there are a few things we need to have in place:

  • Basic knowledge of PHP and the Laravel framework.
  • Composer: Composer is the dependency manager for the PHP programming language that provides a standard format for managing dependencies of PHP software and required libraries. We’ll use it to install some packages in the project directory. Download it here.
  • AWS Account: We need an AWS account to use AWS S3. Open a free account here. (Payment details might be required)
  • Understanding of HTTP clients like Postman.

Installation and Setup

Creating an IAM User

AWS Identity and Access Management (IAM) is a service that enables users to manage access to AWS resources securely. IAM enables users to create and manage AWS users and groups, and to assign permissions to these entities to access AWS resources.

When a new AWS account is created, an initial IAM user is automatically generated. This user has full access to all AWS services and resources and can be used to manage the account and its associated resources. However, it is not recommended to utilize the root user for day-to-day tasks due to security reasons. It is advisable to create additional IAM users with minimal privilege access to carry out routine activities. This is considered a best practice for maintaining the security of AWS resources.

Follow these steps in the AWS IAM console to create a new user with access only to the S3 service:

  1. Go to the IAM console and select "Add User".
  2. Enter the desired username and select "Attach policy directly" in the "Permissions" tab.
  3. Search for "S3" and select the "AmazonS3FullAccess" policy.
  4. Click on "Create User".
  5. Make sure to save the user's access key ID and secret access key, as the secret key can only be viewed once.

Creating a New Laravel Project

Let’s start by creating a new Laravel project. To do that, run the following command in the terminal.

composer create-project laravel/laravel file-upload-api 9.5
Enter fullscreen mode Exit fullscreen mode

By executing this command, we are using Composer to create a new Laravel 9.5 project called file-upload-api into the specified directory.

Next, we check our project is running correctly by starting our server.

Windows users should run the below in their terminal.

cd file_upload_api
php artisan serve
Enter fullscreen mode Exit fullscreen mode

Mac users should run the code below.

cd file_upload_api
valet park
Enter fullscreen mode Exit fullscreen mode

Laravel Valet is a development environment for macOS that always configures Mac to run Nginx in the background. For a comprehensive guide on setting up Laravel Valet, click here.

Updating the Env File

Next, we’ll open the .env file and update it with the credentials gotten earlier.

    AWS_ACCESS_KEY_ID= 
    AWS_SECRET_ACCESS_KEY=
    AWS_DEFAULT_REGION=<region_name> // us-east-1 is default
    AWS_BUCKET=<bucket_name> //specify the bucket name here 
    AWS_USE_PATH_STYLE_ENDPOINT=false
Enter fullscreen mode Exit fullscreen mode

Note: Use this link to ensure the right bucket name format is being used.

Installing the AWS SDK

The AWS SDK (Software Development Kit) is a collection of tools, libraries, and documentation provided by Amazon Web Services to make it easier for developers to build applications on top of AWS services. It abstracts away the low-level details of interacting with AWS services. It provides a more developer-friendly interface, allowing developers to focus on their application logic rather than infrastructure management.

To install the AWS SDK, run the following command:

composer require aws/aws-sdk-php
Enter fullscreen mode Exit fullscreen mode

This will install the necessary packages for using AWS S3 with PHP.

Setting up the Controller

Now we’re all set. Let’s now create a new controller by running the command below.

php artisan make:controller uploadController
Enter fullscreen mode Exit fullscreen mode

This command creates a controller named uploadController in the app/Http/Controllers directory.

Open the uploadController file and paste the following code at the top of the file.

    use Aws\S3\S3Client; 
    use Aws\S3\Exception\S3Exception;
Enter fullscreen mode Exit fullscreen mode

The above code snippet imports the S3Client and S3Exception from the previously installed AWS SDK. We will use S3Client to instantiate a new S3Client class, while S3Exception will handle any exceptions that may occur in our application.

Now, having imported all the needed classes, let’s dive into the primary implementation. We will paste the following code into the uploadController class.


    public function UploadFile(Request $request){
        $request->validate([
            'file' => 'required|mimes:pdf,docx,doc,pptx,ppt,xls,xlsx,jpg|max:2048'
        ]);

        // file handling code here
    }
Enter fullscreen mode Exit fullscreen mode

We create a function called uploadFile that contains the entire application logic. Then, we check the file's existence, format, and size using the validate method.

    $file = $request->file('file');

            //create s3 client
            $s3 = new S3Client([
                'version' => 'latest',
                'region'  => env('AWS_DEFAULT_REGION'),
                'credentials' => [
                    'key'    => env('AWS_ACCESS_KEY_ID'),
                    'secret' => env('AWS_SECRET_ACCESS_KEY'),
                ]
            ]);
Enter fullscreen mode Exit fullscreen mode

Next, we retrieve the uploaded file using $request->file('file'). We will then use the S3Client class to create a new S3 client object using the credentials we specified in our .env folder earlier.

Note: It’s good practice to store sensitive details in the .env file in order to protect our site from bad actors.

    $keyname = 'uploads/' . $file->getClientOriginalName();

Enter fullscreen mode Exit fullscreen mode

We are defining the key name for the uploaded file by using a folder named "uploads" and the original filename of the uploaded file. To get the original name of the file uploaded, we use the getClientOriginalName() method.


    if (!$s3->doesBucketExist(env('AWS_BUCKET'))) {
                // Create bucket if it doesn't exist
                try{
                    $s3->createBucket([
                        'Bucket' => env('AWS_BUCKET'),
                    ]);
                } catch (S3Exception $e) {
                    return response()->json([
                        'Bucket Creation Failed' => $e->getMessage()
                    ]);
                }
            }
Enter fullscreen mode Exit fullscreen mode

In the snippet above, we first check if the bucket exists using the doesBucketExist() method. If it does not exist, we try to create it using the createBucket() method, an error is thrown if any exception occurs during the bucket creation process.
If the bucket already exists, we skip the creation step and move on to the file upload. By adding this check, we ensure that we don't attempt to create a bucket that already exists, which could cause errors or unnecessary API calls.

    try {
                $result = $s3->putObject([
                    'Bucket' => env('AWS_BUCKET'),
                    'Key'    => $keyname,
                    'Body'   => fopen($file, 'r'),
                    'ACL'    => 'public-read'
                ]);
Enter fullscreen mode Exit fullscreen mode

Finally, we try to upload the file to S3 using the putObject() method. We pass in the S3 bucket name, key name, file body, and permissions. In this case, we are setting the file to be publicly readable.

    return response()->json([
        'message' => 'File uploaded successfully',
        'file link' => $result['ObjectURL']
    ]);
Enter fullscreen mode Exit fullscreen mode

If the upload is successful, we return a response with the message "File uploaded successfully" and a link to the uploaded file in the S3 bucket.

    } catch (S3Exception $e) {
        return response()->json([
            'Upload Failed' => $e->getMessage()
        ]);
    }
Enter fullscreen mode Exit fullscreen mode

If the upload fails, we catch the S3Exception and return a response with the error message.

Here is the complete code.

    <?php
    namespace App\Http\Controllers;
    use Aws\S3\S3Client;  
    use Illuminate\Http\Request;
    use Aws\S3\Exception\S3Exception;
    use Illuminate\Support\Facades\Storage;
    class UploadController extends Controller
    {
        public function UploadFile(Request $request){

            $request->validate([
                'file' => 'required|mimes:pdf,docx,doc,pptx,ppt,xls,xlsx,jpg|max:2048'
            ]);
            $file = $request->file('file');

            //create s3 client
            $s3 = new S3Client([
                'version' => 'latest',
                'region'  => env('AWS_DEFAULT_REGION'),
                'credentials' => [
                    'key'    => env('AWS_ACCESS_KEY_ID'),
                    'secret' => env('AWS_SECRET_ACCESS_KEY'),
                ]
            ]);
            $keyname = 'uploads/' . $file->getClientOriginalName();
            //create bucket
            if (!$s3->doesBucketExist(env('AWS_BUCKET'))) {
                // Create bucket if it doesn't exist
                try{
                    $s3->createBucket([
                        'Bucket' => env('AWS_BUCKET'),
                    ]);
                } catch (S3Exception $e) {
                    return response()->json([
                        'Bucket Creation Failed' => $e->getMessage()
                    ]);
                }
            }
            //upload file
            try {
                $result = $s3->putObject([
                    'Bucket' => env('AWS_BUCKET'),
                    'Key'    => $keyname,
                    'Body'   => fopen($file, 'r'),
                    'ACL'    => 'public-read'
                ]);
                // Print the URL to the object.
                return response()->json([
                    'message' => 'File uploaded successfully',
                    'file link' => $result['ObjectURL']
                ]);
            } catch (S3Exception $e) {
                return response()->json([
                    'Upload Failed' => $e->getMessage()
                ]);
            }
        }
    }

Enter fullscreen mode Exit fullscreen mode

Setting up the Route

Open the routes/api.php file to create the route that will be used in our application.

    use App\Http\Controllers\UploadController;

    Route::post('/upload', [UploadController::class, 'uploadFile']);
Enter fullscreen mode Exit fullscreen mode

Testing the API

To test our API, we can utilize any HTTP client that we prefer. However, in this article, Postman will be used to demonstrate the process.

Navigate to the endpoint, which is:

  1. Click on “POST”
  2. Go to the “Body” tab, and then select “form-data.”
  3. In the “key” field, type “file” and change the input type to “file.”
  4. In the “value” field, attach the file to be uploaded.
  5. Click the “Send” button.

If everything works fine, the below response will be displayed.

Postman Response

The uploaded file can be downloaded by clicking on the link provided in the response.

Conclusion

In this tutorial, we covered the basics of creating an AWS S3 bucket, creating a Laravel API endpoint for file uploads, uploading files to S3 using the AWS SDK and testing the API using Postman. Additionally, We explored how to create an IAM user on AWS and learned how to handle errors and exceptions when uploading files.

By following this tutorial, developers can create a file upload API that is both robust and scalable, making it ideal for use in production environments. Overall, this tutorial is an excellent starting point for anyone looking to create a file upload API with Laravel 9 and AWS S3.

Resources

Here are some resources for further reading.

  1. Using S3 for File Storage in Laravel
  2. AWS PHP SDK Documentation

Top comments (0)