901
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
Crear un proyecto de laravel con el siguiente comando:
composer create-project laravel/laravel project-minio
composer require league/flysystem-aws-s3-v3
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
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, ],
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);
}
}
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');
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');
É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
php artisan serve
Y ahora navegar al enlace: http://127.0.0.1:8000/ para comenzar a subir archivos.
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.