View on GitHub

docker-devbox

[DEV] Docker stacks to quickly setup a dev environment and test some tools.

Using MinIO S3 object storage in a PHP Symfony project

This page explains how to configure flysystem-bundle use Flysystem to ease migration from a local / NFS storage to an S3 object storage.

Dependencies

composer require league/flysystem-bundle
composer require league/flysystem-async-aws-s3

Configuration

services:
    s3_client:
        class: 'AsyncAws\S3\S3Client'
        arguments:
            -  endpoint: '%env(S3_ENDPOINT)%'
               accessKeyId: '%env(S3_ACCESS_KEY)%'
               accessKeySecret: '%env(S3_SECRET_KEY)%'
               # required for minio (avoids access to {bucket}.minio-s3.dev.localhost)
               pathStyleEndpoint: true

flysystem:
    storages:
        default.storage:
            adapter: 'local'
            options:
                directory: '%kernel.project_dir%/var/data'
        data.storage:
            adapter: 'asyncaws'
            options:
                client: 's3_client'
                bucket: '%env(S3_BUCKET_DATA)%'

with the following content in .env :

S3_ENDPOINT=https://minio-s3.dev.localhost
S3_ACCESS_KEY=ToBeConfigured
S3_SECRET_KEY=ToBeConfigured

S3_BUCKET_DATA=qtw-data
flysystem:
    storages:
        default.storage:
            adapter: 'local'
            options:
                directory: '%kernel.project_dir%/var/data'
        data.storage:
            adapter: 'local'
            options:
                directory: '%kernel.project_dir%/tests/DATA'

Usage

Use dependency injection with FilesystemOperator $defaultStorage and FilesystemOperator $dataStorage.

For example in a controller displaying markdown (https://www.quadtreeworld.net/data/) :

<?php

namespace App\Controller;

use App\Filesystem\dataStorage;
use App\Helper\Strings;
use League\Flysystem\FilesystemOperator;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\Routing\Annotation\Route;

/**
 * Exploration du système de fichier dataStorage avec
 * rendu des markdown.
 */
class DataController extends AbstractController
{
    /**
     * @Route("/data/{path}", name="data_get", requirements={"path"=".*"})
     */
    public function getAction(
        FilesystemOperator $dataStorage,
        string $path = ''
    ): Response {
        if (!empty($path) && !$dataStorage->has($path)) {
            throw $this->createNotFoundException(sprintf("'%s' not found", $path));
        }

        if ($dataStorage->directoryExists($path)) {
            $items = $dataStorage->listContents($path);

            return $this->render('data/list.html.twig', [
                'path' => $path,
                'items' => $items,
            ]);
        } elseif (Strings::endsWith($path, '.md')) {
            $markdownContent = $dataStorage->read($path);
            $parsedown = new \Parsedown();
            $content = $parsedown->text($markdownContent);

            return $this->render('data/markdown.html.twig', [
                'path' => $path,
                'parentPath' => Strings::parentPath($path),
                'content' => $content,
            ]);
        } else {
            $stream = $dataStorage->readStream($path);
            $response = new StreamedResponse();
            $response->headers->set('Content-Type', ''.$dataStorage->mimeType($path));
            $response->headers->set('Content-Length', ''.$dataStorage->fileSize($path));
            $response->setCallback(function () use ($stream) {
                if (0 !== ftell($stream)) {
                    rewind($stream);
                }
                fpassthru($stream);
                fclose($stream);
            });

            return $response;
        }
    }
}

Resources