Storage

Note

Not part of core. Install it separately:

composer require kinetis/storage

File storage against League\Flysystem’s FilesystemOperator interface — read, write, delete, and list files through one interface, swappable to a different backend with no application-code changes. The local backend runs every operation without blocking the rest of your application. S3 (and S3-compatible services) is a second, equally non-blocking backend — see Storage (S3).

use Kinetis\Storage\FilesystemFactory;

$storage = FilesystemFactory::fromConfig($config);

$storage->write('avatars/user-42.png', $imageContents);
$contents = $storage->read('avatars/user-42.png');
$storage->delete('avatars/user-42.png');

$storage is a plain League\Flysystem\FilesystemOperator — any existing Flysystem knowledge or tooling applies directly; there’s no Kinetis-specific interface wrapping it.

Configuring

FILESYSTEM_DRIVER=local
FILESYSTEM_ROOT=/var/app/storage

FILESYSTEM_DRIVER defaults to local — the only driver this package implements. FILESYSTEM_ROOT is required; there’s no sane default to guess, since a wrong one could write files somewhere unintended.

Named connections

$backups = FilesystemFactory::fromConfig($config, 'backups');
FILESYSTEM_BACKUPS_DRIVER=local
FILESYSTEM_BACKUPS_ROOT=/var/app/backups

Following Configuration’s named-connection convention: 'default' reads the plain FILESYSTEM_* keys above; any other name reads FILESYSTEM_{NAME}_* instead. A named filesystem is never autowired by type — resolve it explicitly, or construct it directly, wherever it’s needed.

Streaming an upload

Dispatcher already resolves UploadedFileInterface parameters (see Routing & Validation); its stream feeds writeStream() directly:

use Kinetis\Http\Attributes\Post;
use League\Flysystem\FilesystemOperator;
use Psr\Http\Message\UploadedFileInterface;

final readonly class AvatarController
{
    public function __construct(private FilesystemOperator $storage) {}

    #[Post('/avatar')]
    public function store(UploadedFileInterface $avatar): array
    {
        $this->storage->writeStream(
            "avatars/{$avatar->getClientFilename()}",
            $avatar->getStream()->detach(),
        );

        return ['status' => 'stored'];
    }
}

Metadata and visibility

use League\Flysystem\Visibility;

$storage->fileExists('avatars/user-42.png');
$storage->fileSize('avatars/user-42.png');
$storage->lastModified('avatars/user-42.png');
$storage->mimeType('avatars/user-42.png');

$storage->setVisibility('avatars/user-42.png', Visibility::PUBLIC);

listContents($path, deep: true) walks a directory tree, yielding FileAttributes/DirectoryAttributes for each entry:

foreach ($storage->listContents('avatars', deep: true) as $item) {
    $item->path();
    $item->isFile();
}

See also

  • Storage (S3) — the S3-backed driver, plugged in via the same FilesystemFactory.

  • Configuration — the named-connection convention FilesystemFactory builds on.

  • Persistence — MySQL, Postgres, and Redis, which run the same way: without blocking the rest of your application.

  • Routing & ValidationUploadedFileInterface parameter binding.