league/flysystem-ziparchive
Flysystem ZipArchive adapter sub-split. Use it to work with ZIP files via Flysystem’s filesystem abstraction. This repo is read-only for packaging; file issues and pull requests on the main Flysystem project: https://github.com/thephpleague/flysystem
Installation
composer require league/flysystem-ziparchive
Ensure ext-zip is enabled in your php.ini.
Basic Usage
use League\Flysystem\Filesystem;
use League\Flysystem\ZipArchive\ZipArchiveAdapter;
$adapter = new ZipArchiveAdapter('/path/to/your/archive.zip');
$filesystem = new Filesystem($adapter);
// Write a file
$filesystem->write('file.txt', 'Hello, Zip!');
// Read a file
echo $filesystem->read('file.txt');
First Use Case Dynamically generate a ZIP file on-the-fly for user downloads:
$zipAdapter = new ZipArchiveAdapter('downloads/archive_' . uniqid() . '.zip');
$filesystem = new Filesystem($zipAdapter);
// Add files from another filesystem (e.g., local or S3)
$sourceFilesystem->readStream('document.pdf')
->writeTo($filesystem, 'document.pdf');
return response()->streamDownload(
fn () => $zipAdapter->getZipArchive()->getStream(),
'archive.zip'
);
Temporary ZIP Files
Use ZipArchiveAdapter for ephemeral ZIP files (e.g., report generation):
$tempPath = tempnam(sys_get_temp_dir(), 'zip_');
$adapter = new ZipArchiveAdapter($tempPath, 'w');
$filesystem = new Filesystem($adapter);
// Add files...
$filesystem->write('report.csv', $csvData);
// Return file for download
return response()->download($tempPath);
Integration with Other Adapters Chain adapters to read from one storage system and write to ZIP:
$localAdapter = new LocalAdapter();
$zipAdapter = new ZipArchiveAdapter('backup.zip');
$zipFilesystem = new Filesystem($zipAdapter);
// Copy all files from local to ZIP
foreach ($localFilesystem->listContents('', true) as $file) {
$localFilesystem->readStream($file->path())
->writeTo($zipFilesystem, $file->path());
}
Streaming Large Files Avoid memory issues by streaming files directly into the ZIP:
$filesystem->writeStream('large_file.iso', fopen('/path/to/large_file.iso', 'r'));
Metadata Handling Preserve file metadata (e.g., timestamps) when adding files:
$filesystem->write('file.txt', 'Content', [
'visibility' => 'public',
'timestamp' => time(),
]);
Service Provider Binding Bind the adapter to Laravel’s container for reuse:
public function register()
{
$this->app->singleton('zipFilesystem', function ($app) {
$path = storage_path('app/temp/backup.zip');
return new Filesystem(new ZipArchiveAdapter($path));
});
}
Artisan Commands Use ZIP adapters in commands for batch operations:
public function handle()
{
$zipAdapter = new ZipArchiveAdapter('storage/app/backups/backup_' . date('Y-m-d') . '.zip');
$filesystem = new Filesystem($zipAdapter);
// Add all files from a directory
$this->addDirectoryToZip($filesystem, 'storage/app/logs');
$this->info('Backup created: ' . $zipAdapter->getPathname());
}
File Uploads Handle user uploads directly into ZIP archives:
public function store(Request $request)
{
$zipAdapter = new ZipArchiveAdapter('storage/app/uploads/archive.zip');
$filesystem = new Filesystem($zipAdapter);
foreach ($request->file('files') as $file) {
$filesystem->write(
$file->hashName(),
file_get_contents($file->getRealPath())
);
}
return back()->with('success', 'Files uploaded to archive!');
}
ZipArchive Extension Requirements
ext-zip is installed and enabled. Test with:
php -m | grep zip
league/flysystem-zipstream for PHP < 5.6 or without ext-zip.File Path Limitations
// Bad: $filesystem->write('very/long/path/file.txt', '...');
// Good: $filesystem->write('documents/file.txt', '...');
Concurrent Access
// ❌ Race condition
$filesystem1->write('file.txt', '...');
$filesystem2->write('file.txt', '...');
// ✅ Safe
$filesystem->write('file.txt', '...');
Memory Limits
memory_limit. Stream files or increase limits:
ini_set('memory_limit', '512M');
Case Sensitivity
$normalizedPath = strtolower($path); // For cross-platform compatibility
Verify ZipArchive Errors Check for silent failures by enabling error reporting:
$zip = new ZipArchive();
if ($zip->open('archive.zip', ZipArchive::CREATE) !== true) {
throw new \RuntimeException('Failed to create ZIP: ' . $zip->getStatusString());
}
Inspect ZIP Contents
Use ZipArchive::getFromIndex() to debug missing files:
$zip = $adapter->getZipArchive();
for ($i = 0; $i < $zip->numFiles; $i++) {
echo $zip->getFromIndex($i) . "\n";
}
Handle Corrupted ZIPs Always validate the ZIP before operations:
if (!$adapter->getZipArchive()->status === ZipArchive::ER_OK) {
throw new \RuntimeException('Invalid or corrupted ZIP file');
}
Custom ZipArchive Configuration
Extend ZipArchiveAdapter for custom behavior:
class CustomZipAdapter extends ZipArchiveAdapter
{
public function __construct(string $path, int $flags = ZipArchive::CREATE)
{
parent::__construct($path, $flags | ZipArchive::OVERWRITE);
}
}
Event Listeners Hook into file operations (e.g., log ZIP modifications):
$filesystem->addPlugin(new \League\Flysystem\Plugin\EventDispatcherPlugin());
$dispatcher = $filesystem->getPlugin('eventDispatcher');
$dispatcher->addListener('preWrite', function ($event) {
Log::debug('Writing to ZIP: ' . $event->file());
});
Fallback Adapters Implement a fallback for unsupported operations:
class FallbackZipAdapter extends ZipArchiveAdapter
{
public function writeStream($path, $resource, array $config = [])
{
if (!is_resource($resource)) {
throw new \InvalidArgumentException('Stream must be a resource');
}
return parent::writeStream($path, $resource, $config);
}
}
Laravel Filesystem Integration
Register the adapter as a custom disk in config/filesystems.php:
'disks' => [
'zip' => [
'driver' => 'custom',
'adapter' => \League\Flysystem\ZipArchive\ZipArchiveAdapter::class,
'path' => storage_path('app/backups/archive.zip'),
],
],
Performance Optimization
$adapter = new ZipArchiveAdapter('archive.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE);
$adapter->getZipArchive()->addEmptyDir('videos');
$adapter->getZipArchive()->addFile('video.mp4', 'videos/video.mp4', ZipArchive::FL_ENC_GUESS, 'video/mp4');
How can I help you explore Laravel packages today?