Using Minio on Laravel + Vapor Locally
Testing Laravel Vapor application with file uploads locally can be tricky. It is possible to setup S3 bucket to receive file from localhost by adding required CORS configuration but now you have the alternative of using Minio.
Prequisite
- Setup
laravel/vapor-core
using Composer. - Setup
laravel-vapor
using NPM to allow file uploading.
Setup Minio
You can setup Minio locally using Docker or Homestead, however for this demonstration I will be using Takeout. Remember that you need to update config/filesystems.php
to include minio
disk configuration:
'minio' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION'),
'bucket' => env('AWS_BUCKET'),
'endpoint' => env('AWS_ENDPOINT'),
'url' => env('AWS_URL'),
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', true),
],
Also don't forget to update .env
:
FILESYSTEM_DRIVER=minio
FILESYSTEM_CLOUD=minio
AWS_ACCESS_KEY_ID=minioadmin
AWS_SECRET_ACCESS_KEY=minioadmin
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=local
AWS_ENDPOINT="http://127.0.0.1:9000"
AWS_USE_PATH_STYLE_ENDPOINT=(true)
Setup Laravel
Next we need to setup Laravel to authorize file uploads. You can refer to Storage > Authorization documentation. For testing you can just do the following on AppServiceProvider
:
if ($this->app->isLocal()) {
Gate::define('uploadFiles', fn ($user) => true);
}
Finally you need to map SignedStorageUrlController
with the following files:
use App\Http\Controllers\SignedStorageUrlController;
use Laravel\Vapor\Contracts\SignedStorageUrlController as SignedStorageUrlControllerContract;
if ($this->app->isLocal()) {
$this->app->singleton(
SignedStorageUrlControllerContract::class,
SignedStorageUrlController::class
);
}
And the following Gist should be published to app/Http/Controller/SignedStorageUrlController.php
:
<?php | |
namespace App\Http\Controllers; | |
use Aws\S3\S3Client; | |
use Illuminate\Http\Request; | |
use Illuminate\Support\Facades\Gate; | |
use Illuminate\Support\Str; | |
use InvalidArgumentException; | |
use Laravel\Vapor\Contracts\SignedStorageUrlController as SignedStorageUrlControllerContract; | |
class SignedStorageUrlController extends \Illuminate\Routing\Controller implements SignedStorageUrlControllerContract | |
{ | |
/** | |
* Create a new signed URL. | |
* | |
* @param \Illuminate\Http\Request $request | |
* @return \Illuminate\Http\Response | |
*/ | |
public function store(Request $request) | |
{ | |
Gate::authorize('uploadFiles', [ | |
$request->user(), | |
$bucket = $request->input('bucket') ?: config('filesystems.disks.minio.bucket'), | |
]); | |
$client = $this->storageClient(); | |
$uuid = (string) Str::uuid(); | |
$signedRequest = $client->createPresignedRequest( | |
$this->createCommand($request, $client, $bucket, $key = ('tmp/'.$uuid)), | |
'+10 minutes' | |
); | |
return response()->json([ | |
'uuid' => $uuid, | |
'bucket' => $bucket, | |
'key' => $key, | |
'url' => (string) $signedRequest->getUri(), | |
'headers' => $this->headers($request, $signedRequest), | |
], 201); | |
} | |
/** | |
* Create a command for the PUT operation. | |
* | |
* @param \Illuminate\Http\Request $request | |
* @param \Aws\S3\S3Client $client | |
* @param string $bucket | |
* @param string $key | |
* @return \Aws\Command | |
*/ | |
protected function createCommand(Request $request, S3Client $client, $bucket, $key) | |
{ | |
return $client->getCommand('putObject', array_filter([ | |
'Bucket' => $bucket, | |
'Key' => $key, | |
])); | |
} | |
/** | |
* Get the headers that should be used when making the signed request. | |
* | |
* @param \Illuminate\Http\Request $request | |
* @param \GuzzleHttp\Psr7\Request | |
* @return array | |
*/ | |
protected function headers(Request $request, $signedRequest) | |
{ | |
return array_merge( | |
$signedRequest->getHeaders(), | |
[ | |
'Content-Type' => $request->input('content_type') ?: 'application/octet-stream', | |
] | |
); | |
} | |
/** | |
* Get the S3 storage client instance. | |
* | |
* @return \Aws\S3\S3Client | |
*/ | |
protected function storageClient() | |
{ | |
$config = [ | |
'region' => config('filesystems.disks.minio.region'), | |
'version' => 'latest', | |
'use_path_style_endpoint' => true, | |
'url' => config('filesystems.disks.minio.endpoint'), | |
'endpoint' => config('filesystems.disks.minio.endpoint'), | |
'credentials' => [ | |
'key' => config('filesystems.disks.minio.key'), | |
'secret' => config('filesystems.disks.minio.secret'), | |
], | |
]; | |
return S3Client::factory($config); | |
} | |
} |