Contexto
Recientemente fue necesario separar una aplicación web de los archivos que genera, por lo que se buscó y se encontró MinIO para solventar el almacenamiento de archivos. (Instalación en docker) En éste post mostraré cómo integrar MinIO con un proyecto Laravel
1.- Crear proyecto Laravel
Crear un proyecto de laravel con el siguiente comando:
composer create-project laravel/laravel project-minio
2.- Instalar el paquete league/flysystem-aws-s3-v3:
composer require league/flysystem-aws-s3-v3
3.- Ahora configurar los accesos en el archivo .env
Los datos de MINIO_SECRET_ACCESS_KEY y MINIO_ACCESS_KEY_ID se obtienen cuando se creó el Service Account del usuario que se registró en el post: instalación de MinIO si no se anotó o guardó, se puede crear un nuevo service account.
MINIO_ACCESS_KEY_ID=pzwnKFgyD3pYD3DQ MINIO_SECRET_ACCESS_KEY=J7bhE7cPRaMkc0nQoNTiakyHKRy1Aa33 MINIO_DEFAULT_REGION=us-east-1 MINIO_BUCKET=files FILESYSTEM_CLOUD=minio MINIO_URL=http://127.0.0.1:9000/${MINIO_BUCKET} MINIO_ENDPOINT=http://127.0.0.1:9000 MINIO_USE_PATH_STYLE_ENDPOINT=true
Y buscar la configuración que de nombre FILESYSTEM_DISK y cambiar a la siguiente forma:
FILESYSTEM_DISK=minio
4.- Editar el archivo config/filesystems.php
Acceder y modificar el archivo config/filesystems.php añadiendo la siguiente línea:
'cloud' => env('FILESYSTEM_CLOUD', 's3'),
Y en la sección de disks crear un nuevo array:
'minio' => [ 'driver' => 's3', 'key' => env('MINIO_ACCESS_KEY_ID'), 'secret' => env('MINIO_SECRET_ACCESS_KEY'), 'region' => env('MINIO_DEFAULT_REGION'), 'bucket' => env('MINIO_BUCKET'), 'url' => env('MINIO_URL'), 'endpoint' => env('MINIO_ENDPOINT'), 'throw' => true, ],
5.- Crear controlador FileController
php artisan make:controller FileController
Y añadir el siguiente contenido:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
class FileController extends Controller
{
public function index(Request $request)
{
$data['archivos'] = [];
$data['total'] = 0;
return view('main', $data);
}
public function store(Request $request)
{
$data = [];
$archivos = [];
$data['total'] = 0;
if ($request->hasfile('files')) {
foreach ($request->file('files') as $key => $file) {
$data['total'] ++;
$nombreArchivo = Str::random(4).'-'.Str::random(4).'.'.$file->getClientOriginalExtension();
$contenido = file_get_contents($file);
$path = Storage::cloud()->put($nombreArchivo, $contenido);
$archivos[] = $nombreArchivo;
}
}
$data['archivos'] = $archivos;
return view('main', $data);
}
}
6.- Editar el archivo routes/api.php
Abrir el archivo routes/api.php y añadir las siguientes rutas:
Route::post('file','App\Http\Controllers\FileController@store'); Route::get('file','App\Http\Controllers\FileController@show');
7.- Editar el archivo routes/web.php
Route::get('/', 'App\Http\Controllers\FileController@index')->name('/'); Route::get('/', 'App\Http\Controllers\FileController@index')->name('home'); Route::post('store', 'App\Http\Controllers\FileController@store')->name('store');
8.- Crear la vista views/main.blade.php
Ésta vista contendrá un formulario simple para subir archivos, y en automático se suben a MinIO:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Storage MinIO Laravel</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.3/font/bootstrap-icons.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css"
integrity="sha512-1ycn6IcaQQ40/MKBW2W4Rhis/DbILU74C1vSrLJxCq57o941Ym01SwNsOMqvEBFlcgUa6xLiPY/NS5R+E6ztJQ=="
crossorigin="anonymous" referrerpolicy="no-referrer" />
</head>
<body>
<div class="container">
<header
class="d-flex flex-wrap align-items-center justify-content-center justify-content-md-between py-3 mb-4 border-bottom">
<a href="/" class="d-flex align-items-center col-md-3 mb-2 mb-md-0 text-dark text-decoration-none">
<svg class="bi me-2" width="40" height="32" role="img" aria-label="Bootstrap">
<use xlink:href="#bootstrap"></use>
</svg>
</a>
<ul class="nav col-12 col-md-auto mb-2 justify-content-center mb-md-0">
<li><a href="#" class="nav-link px-2 link-secondary">Home</a></li>
<li><a href="#" class="nav-link px-2 link-dark">About</a></li>
</ul>
<div class="col-md-3 text-end">
<button type="button" class="btn btn-outline-primary me-2">Login</button>
<button type="button" class="btn btn-primary">Sign-up</button>
</div>
</header>
</div>
<div class="container">
<div class="row">
<div class="col-md-12">
<form action="{{route('store')}}" method="post" accept-charset="utf-8" enctype="multipart/form-data">
@csrf
<div class="row">
<div class="col-md-4">
<div class="input-group mb-3">
<label class="input-group-text" for="file1">Archivo uno</label>
<input type="file" class="form-control" name="files[]" id="file1" required="" multiple="">
</div>
</div>
<div class="col-md-4">
<button type="submit" class="btn btn-success float-end">Enviar</button>
</div>
</div>
</form>
</div>
<div class="col-md-12">
<span>Total archivos: {{$total}}</span>
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Archivo</th>
</tr>
</thead>
<tbody>
@forelse ($archivos as $archivo)
<tr>
<td>{{$loop->iteration}}</td>
<td>
<a href="{{ asset(env('MINIO_URL')).'/'.$archivo}}" target="_blank">{{$archivo}}</a>
</td>
</tr>
@empty
<tr>
<td colspan="2" class="text-center">Sin archivos recientes</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"
integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN"
crossorigin="anonymous"></script>
</body>
</html>
Al iniciar en MinIO y navegar al Bucket/Files se podrán visualizar los archivos subidos
9.- Probar el proyecto
php artisan serve
Y ahora navegar al enlace: http://127.0.0.1:8000/ para comenzar a subir archivos.
10.- Conclusión
En éste ejemplo básico, se suben los archivos a MinIO, sin embargo no se guardan los enlaces a una base de datos por parte del proyecto de laravel, sin embargo, éste proyecto puede servir como base e implementar las funcionalidades específicas que se requieran para cada caso.