logo
Secciones

Entradas del blog


Integrar MinIO Storage con proyecto Laravel

Sin comentarios

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.

Comentarios:

Conoce la red social linuxClick
Redes sociales
Accesos directos