league/flysystem
Flysystem is a filesystem abstraction for PHP that lets you read, write, and manage files through a unified API across local disks and cloud storage (S3, FTP, SFTP, etc.). Swap adapters without changing app code, with consistent paths, streams, and visibility.
Installation:
composer require league/flysystem
For common adapters (e.g., S3, local):
composer require league/flysystem-aws-s3-s3v3
composer require league/flysystem-local
Basic Configuration:
Define adapters in config/filesystems.php (Laravel already includes FlySystem support):
'disks' => [
's3' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION'),
'bucket' => env('AWS_BUCKET'),
'url' => env('AWS_URL'),
],
'local' => [
'driver' => 'local',
'root' => storage_path('app'),
],
],
First Use Case:
Access files via the Storage facade:
use Illuminate\Support\Facades\Storage;
// Write a file
Storage::disk('s3')->put('file.txt', 'Hello, FlySystem!');
// Read a file
$contents = Storage::disk('local')->get('file.txt');
// List files
$files = Storage::disk('s3')->files('path/to/dir');
File Operations:
put(), write(), or writeStream() for files/strings/streaming.
Storage::disk('s3')->put('report.pdf', $fileContents);
Storage::disk('local')->write('log.txt', 'New log entry');
get(), read(), or readStream().
$content = Storage::disk('s3')->get('report.pdf');
Storage::disk('local')->readStream('log.txt');
delete() for single files or deleteDirectory() for directories.
Storage::disk('s3')->delete('old-file.txt');
Storage::disk('local')->deleteDirectory('temp');
Directory Operations:
makeDirectory() or directoryExists().
Storage::disk('s3')->makeDirectory('uploads/2023');
if (Storage::disk('local')->directoryExists('cache')) { ... }
files(), directories(), or allFiles().
$files = Storage::disk('s3')->files('uploads');
$dirs = Storage::disk('local')->directories('storage');
Metadata and URLs:
metadata(), mimeType(), size(), or lastModified().
$metadata = Storage::disk('s3')->metadata('file.txt');
$mime = Storage::disk('local')->mimeType('file.txt');
url() or temporaryUrl() (for signed URLs).
$url = Storage::disk('s3')->url('public/image.jpg');
$tempUrl = Storage::disk('s3')->temporaryUrl('private/file.pdf', now()->addHours(1));
Copy/Move/Rename:
copy(), move(), or rename() with source/destination paths.
Storage::disk('s3')->copy('old.txt', 'backup/old.txt');
Storage::disk('local')->move('file1.txt', 'file2.txt');
Checksums:
checksum() for file integrity verification.
$checksum = Storage::disk('s3')->checksum('file.txt');
MountManager for Multiple Adapters: Combine adapters (e.g., local fallback for S3):
use League\Flysystem\MountManager;
$mountManager = new MountManager([
's3' => new AwsS3Adapter(...),
'local' => new LocalAdapter(...),
]);
$mountManager->mount('backup', $mountManager->getAdapter('local'));
Decorators for Extensions: Extend functionality with decorators (e.g., logging, caching):
use League\Flysystem\Cached\CachedAdapter;
use League\Flysystem\Cached\Storage\FilesystemAdapterStorage;
$cache = new FilesystemAdapterStorage(storage_path('cache/flysystem'));
$adapter = new CachedAdapter($s3Adapter, $cache);
Async Operations:
Use AsyncAwsS3Adapter for non-blocking S3 operations:
$asyncAdapter = new AsyncAwsS3Adapter($asyncAwsS3Client, 'bucket');
Visibility and Permissions:
Configure visibility (e.g., public, private) during uploads:
Storage::disk('s3')->put('file.txt', 'content', [
'visibility' => 'public',
]);
Streaming Large Files:
Use writeStream()/readStream() for memory efficiency:
Storage::disk('s3')->writeStream('large-video.mp4', fopen('local-video.mp4', 'r'));
Path Handling:
'dir/' vs 'dir').storage_path('app')).PathPrefixingAdapter to add prefixes to all paths:
$adapter = new PathPrefixingAdapter($localAdapter, 'prefix/');
Visibility and Permissions:
private. Explicitly set visibility in options:
Storage::disk('s3')->put('file.txt', 'content', ['visibility' => 'public']);
LocalAdapter with visibility config.Directory Listing Quirks:
allFiles() to include hidden files.files()/directories() with glob patterns:
$files = Storage::disk('s3')->files('*.jpg');
Checksums:
InMemory). Fall back to ad-hoc generation:
try {
$checksum = Storage::disk('s3')->checksum('file.txt');
} catch (\League\Flysystem\ChecksumAlgoIsNotSupported) {
$checksum = md5(Storage::disk('s3')->read('file.txt'));
}
Connection Management:
disconnect() to free resources:
$sftpAdapter->disconnect();
Temporary URLs:
temporaryUrl() with a Carbon timestamp:
$url = Storage::disk('s3')->temporaryUrl('file.pdf', now()->addMinutes(30));
s3:GetObject permissions.Race Conditions:
makeDirectory() with recursive: true to avoid race conditions:
Storage::disk('s3')->makeDirectory('path/to/dir', 0777, true);
Enable Debugging:
debug: true in adapter configs (e.g., SFTP) to log connection issues.Handle Exceptions:
League\Flysystem\UnableToReadFile, League\Flysystem\ConnectionException):
try {
Storage::disk('s3')->get('file.txt');
} catch (\League\Flysystem\FileNotFoundException $e) {
Log::error('File not found: ' . $e->getMessage());
}
Adapter-Specific Issues:
ListObjectsV2 for large directories (avoids pagination limits).ssh-rsa).chmod -R 777 storage).Logging:
How can I help you explore Laravel packages today?