Large file uploads to an S3 bucket done neatly in Laravel
Tags: laravel, php, uploads, macro
Published: 2020-02-24 02:35:42

---
title: Large file uploads to an S3 bucket done neatly in Laravel
published: true
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/saumruvm20jdrj2yk64i.jpg
description: Using a macro with Flysystem's S3 adapter, a large file upload can be streamed to your bucket with one line of code.
tags: laravel, php, uploads, macro
---
Lately I've been looking at ways to reduce the amount of clutter in my Laravel controllers, and found macros can be super useful for wrapping reusable snippets of code behind an easily accessible facades.

If you'd like to learn more about Laravel macros, here's a great primer, with a list of "Macroable" classes:
https://tighten.co/blog/the-magic-of-laravel-macros

# Dealing with large files
The best way to reliably upload large files to an S3 bucket in Laravel is with the Flysystem S3 adapter, specifically using the `writeStream` and `putStream` methods.

<strong>Special note:</strong> You are almost certainly going to need to tweak the host's php.ini configuration, specifically:
* `post_max_size`
* `upload_max_filesize`


# Setting up
The first thing I would recommend, if you don't do this already, is to set up a Service Provider specifically for your macros. To do this:
* Run `php artisan make:provider MacroServiceProvider`
* Add `App\Providers\MacroServiceProvider::class` to your project's `app.php` config file

Next, install the Flysystem AWS S3 Adapter: 
`composer require league/flysystem-aws-s3-v3`

Finally, you'll want to ensure these four values are set in your project's `.env` file:
* AWS_ACCESS_KEY=YOURACCESSKEY
* AWS_SECRET_ACCESS_KEY=YOURSECRETACCESSKEY
* AWS_REGION=aws-bucket-region
* AWS_S3_BUCKET=name.of.s3.bucket


# Create the macro
For this macro, we are going to extend the built in `File` component so we can use its facade. 

Within `App\Providers\MacroServiceProvider`, let's add a closure to the `boot` method with four parameters:
* `path`: Path within the bucket to save the file
* `filename`: Filename you want to save as
* `file`: The file object from `$request->file('input_name')`
* `overWrite`: Whether or not you want to overwrite the file if a file with the same name already exists

Here's the code:

```php
<?php

namespace App\Providers;

use Aws\S3\S3Client;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\File;
use League\Flysystem\AwsS3v3\AwsS3Adapter;
use League\Flysystem\Filesystem;

class MacroServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        File::macro('streamUpload', function($path, $fileName, $file, $overWrite = true) {
            // Set up S3 connection.
            $resource = fopen($file->getRealPath(), 'r+');
            $config = Config::get('filesystems.disks.s3');
            $client = new S3Client([
                'credentials' => [
                    'key'    => $config['key'],
                    'secret' => $config['secret'],
                ],
                'region' => $config['region'],
                'version' => 'latest',
            ]);

            $adapter = new AwsS3Adapter($client, $config['AWS_S3_BUCKET'], $path);
            $filesystem = new Filesystem($adapter);
            
            return $overWrite 
                    ? $filesystem->putStream($fileName, $resource) 
                    : $filesystem->writeStream($fileName, $resource);
        });
    }
}

```

# Update your controller
Now all you need to do is grab the file from the request, set your options, and call the macro:
```php
            // Set file attributes.
            $filepath = 'upload/destination/files';
            $file = $request->file('uploaded-file');
            $filename = $request->input('name'); // Hidden input with a generated value

            // Upload to S3, overwriting if filename exists.
            File::streamUpload($filepath, $filename, $file, true);

```
Aaand that's it! Happy uploading!