Middleware to save or read responses using Flysystem

v2.0.0 2019-11-30 15:47 UTC

This package is auto-updated.

Last update: 2020-06-30 01:19:21 UTC


Latest Version on Packagist Software License Build Status Quality Score Total Downloads

Middleware to save or read responses from files. It uses Flysystem as filesystem handler, so you can use not only a local directories, but also any other adapter like ftp, sftp, dropbox, etc... This package includes the following components:



This package is installable and autoloadable via Composer as middlewares/filesystem.

composer require middlewares/filesystem




To read the response body from a file under the following conditions:

  • Only GET methods are allowed, returning a 405 code otherwise.
  • If the request path has no extension, assume it's a directory and append /index.html. For example: if the request path is /post/23, the file used is /post/23/index.html.
  • It can handle gzipped files. For example, if /post/23/index.html does not exists but /post/23/index.html.gz is available and the request header Accept-Encoding contains gzip, returns it.
  • Accept-Ranges is also supported, useful to server big files like videos.

Example using a ftp storage:

use League\Flysystem\Filesystem;
use League\Flysystem\Adapter\Ftp;

$filesystem = new Filesystem(new Ftp([
    'host' => 'ftp.example.com',
    'username' => 'username',
    'password' => 'password',
    'port' => 21,
    'root' => '/path/to/root',
    'passive' => true,
    'ssl' => true,
    'timeout' => 30,

    new Middlewares\Reader($filesystem)

Optionally, you can provide a Psr\Http\Message\ResponseFactoryInterface and Psr\Http\Message\StreamFactoryInterface, that will be used to create the response and stream. If they are not not defined, Middleware\Utils\Factory will be used to detect them automatically.

$responseFactory = new MyOwnResponseFactory();
$streamFactory = new MyOwnStreamFactory();

$reader = new Middlewares\Reader($filesystem, $responseFactory, $streamFactory);


Allows to continue to the next middleware on error (file not found, method not allowed, etc). This allows to create a simple caching system as the following:

$cache = new Flysystem(new Local(__DIR__.'/path/to/files'));

    (new Middlewares\Reader($cache))    //read and returns the cached response...
        ->continueOnError(),            //...but continue if the file does not exists

    new Middlewares\Writer($cache),     //save the response in the cache

    new Middlewares\AuraRouter($route), //create a response using, for example, Aura.Router


Saves the response content into a file if all of the following conditions are met:

  • The method is GET
  • The status code is 200
  • The Cache-Control header does not contain no-cache and no-store

To be compatible with Reader behaviour:

  • If the request path has no extension, assume it's a directory and append /index.html. For example: if the request path is /post/23, the file saved is /post/23/index.html.
  • If the response is gzipped (has the header Content-Encoding: gzip) the file is saved with the extension .gz. For example /post/23/index.html.gz (instead /post/23/index.html).
$filesystem = new Flysystem(new Local(__DIR__.'/storage'));

    new Middlewares\Writer($filesystem)

Optionally, you can provide a Psr\Http\Message\StreamFactoryInterface as the second that will be used to create a new body to the response. If it's not defined, Middleware\Utils\Factory will be used to detect it automatically.

$streamFactory = new MyOwnStreamFactory();

$reader = new Middlewares\Writer($filesystem, $streamFactory);



Both Reader and Writer have a static method as a shortcut to create instances using a directory in the local filesystem, due this is the most common case:


Please see CHANGELOG for more information about recent changes and CONTRIBUTING for contributing details.

The MIT License (MIT). Please see LICENSE for more information.