league/flysystem
League/Flysystem is a filesystem abstraction for PHP that provides a consistent API for local disks and cloud storage like S3. Swap adapters without changing your app, with support for reading/writing files, directories, visibility, and streams.
Installation:
composer require league/flysystem
For a specific adapter (e.g., AWS S3):
composer require league/flysystem-aws-s3-v3
Basic Usage:
use League\Flysystem\Filesystem;
use League\Flysystem\Adapter\Local;
$adapter = new Local('/path/to/storage');
$filesystem = new Filesystem($adapter);
// Write a file
$filesystem->write('file.txt', 'Hello, Flysystem!');
// Read a file
$contents = $filesystem->read('file.txt');
First Use Case:
Replace direct filesystem operations (e.g., file_put_contents, fopen) with Flysystem methods. For example:
// Instead of:
file_put_contents('/path/to/file.txt', 'content');
// Use:
$filesystem->write('file.txt', 'content');
Local, S3, SFTP).Visibility, Cache).File Operations:
// Write, read, delete
$filesystem->write('file.txt', 'content');
$content = $filesystem->read('file.txt');
$filesystem->delete('file.txt');
// Check existence
if ($filesystem->has('file.txt')) {
$size = $filesystem->size('file.txt');
}
Directory Operations:
// Create, list, delete
$filesystem->createDirectory('folder');
$contents = $filesystem->listContents('folder');
$filesystem->deleteDirectory('folder');
Metadata and URLs:
// Get metadata
$metadata = $filesystem->metadata('file.txt');
// Generate public URLs (if supported by adapter)
$url = $filesystem->url('file.txt');
// Temporary URLs (e.g., for S3)
$tempUrl = $filesystem->temporaryUrl('file.txt', Carbon::now()->addHour());
Service Providers:
Bind adapters and filesystems in Laravel’s AppServiceProvider:
public function register()
{
$this->app->singleton('filesystem.s3', function ($app) {
$client = new Aws\S3\S3Client([
'version' => 'latest',
'region' => 'us-east-1',
'credentials' => [
'key' => env('AWS_KEY'),
'secret' => env('AWS_SECRET'),
],
]);
$adapter = new AwsS3V3($client, 'bucket-name');
return new Filesystem($adapter);
});
}
MountManager for Multiple Filesystems:
use League\Flysystem\MountManager;
$mountManager = new MountManager([
'local' => new Filesystem(new Local('/path/to/local')),
's3' => new Filesystem(new AwsS3V3($client, 'bucket-name')),
]);
// Use dynamically
$mountManager->getAdapter('s3')->write('file.txt', 'content');
Decorators for Cross-Cutting Concerns:
use League\Flysystem\Cached\CachedAdapter;
$adapter = new Local('/path/to/storage');
$cachedAdapter = new CachedAdapter($adapter, new FilesystemCache());
$filesystem = new Filesystem($cachedAdapter);
Streaming Large Files:
// Write a stream (e.g., from a database blob or HTTP request)
$filesystem->writeStream('large-file.zip', fopen('php://temp', 'r+', 0, 1024 * 1024));
// Read a stream
$stream = $filesystem->readStream('large-file.zip');
Filesystem Disks:
Configure in config/filesystems.php:
'disks' => [
's3' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'bucket' => 'your-bucket',
'region' => 'us-east-1',
],
],
Use via the Storage facade:
Storage::disk('s3')->put('file.txt', 'content');
Temporary URLs:
$tempUrl = Storage::disk('s3')->temporaryUrl(
'file.txt',
now()->addMinutes(15),
['response-content-disposition' => 'attachment']
);
Symlinks:
// Create a symlink (if adapter supports it)
$filesystem->createSymlink('source.txt', 'link.txt');
Path Normalization:
\ to / on Windows). Ensure paths are consistent.DIRECTORY_SEPARATOR or let Flysystem handle it:
$filesystem->write('folder' . DIRECTORY_SEPARATOR . 'file.txt', 'content');
Visibility Retention:
copy() and move() may not retain visibility (e.g., public/private on S3). Configure explicitly:
$adapter = new AwsS3V3($client, 'bucket', [
'visibility' => 'public',
'retain_visibility' => true,
]);
Directory Listing Quirks:
listContents() carefully:
$contents = $filesystem->listContents('folder', true); // Recursive
Checksum Mismatches:
try {
$checksum = $filesystem->checksum('file.txt');
} catch (ChecksumAlgoIsNotSupported $e) {
// Fallback to ad-hoc generation
}
Connection Leaks:
$adapter->disconnect();
Enable Verbose Logging:
$adapter = new SftpV3('host', 'user', 'pass', [
'debug' => true,
]);
Handle Exceptions:
ConnectionException, UnableToListContents):
try {
$filesystem->listContents('folder');
} catch (UnableToListContents $e) {
Log::error('Failed to list contents: ' . $e->getMessage());
}
Path Prefixing:
PathPrefixingAdapter, ensure paths are prefixed correctly:
$adapter = new PathPrefixingAdapter(new Local('/base'), 'prefix/');
Custom Adapters:
League\Flysystem\Adapter\AbstractAdapter or use DecoratedAdapter:
class CustomAdapter extends AbstractAdapter {
public function write($path, $contents, Config $config) {
// Custom logic
}
}
URL Generators:
League\Flysystem\UrlGenerator\UrlGeneratorInterface for custom URL schemes:
class CustomUrlGenerator implements UrlGeneratorInterface {
public function generate(string $path): string {
return "https://custom.cdn/$path";
}
}
Event Dispatching:
preWrite, postDelete) via decorators or middleware:
$adapter = new EventedAdapter(new Local('/path'), new EventDispatcher());
Async Operations:
AsyncAwsS3), handle promises:
$adapter->writeAsync('file.txt', 'content')->then(
function () { /* Success */ },
function ($e) { /* Failure */ }
);
ListObjectsV2 is used (not ListObjects) for pagination:
$adapter
How can I help you explore Laravel packages today?