Symfony file storage abstraction bundle with support for local and cloud storage providers.
- No database dependency - Pure file storage operations
- S3-compatible - Uses async-aws/s3 for cloud storage
- Multiple devices - Configure and use multiple storage providers simultaneously
- Fully tested - Complete test coverage for all drivers
- Local - Filesystem-based storage
- Cloudflare R2 - S3-compatible object storage
- BackBlaze B2 - S3-compatible object storage
Requires Symfony 8 and PHP 8.4+
composer require cesurapp/storage-bundleCreate config/packages/storage.yaml:
storage:
default: main # Default storage device
devices:
# Local filesystem storage
local:
driver: local
root: "%kernel.project_dir%/storage/files"
# Cloudflare R2 storage
main:
driver: cloudflare
root: /
accessKey: "your_access_key"
secretKey: "your_secret_key"
bucket: "your_bucket_name"
region: "auto"
endPoint: "https://your-account-id.r2.cloudflarestorage.com"
# BackBlaze B2 storage
backblaze:
driver: backblaze
root: /
accessKey: "your_key_id"
secretKey: "your_application_key"
bucket: "your_bucket_name"
region: "us-west-001" # See BackBlaze regionsuse Cesurapp\StorageBundle\Driver\BackBlaze;
BackBlaze::US_WEST_001; // us-west-001
BackBlaze::US_WEST_002; // us-west-002
BackBlaze::US_WEST_003; // us-west-003
BackBlaze::US_WEST_004; // us-west-004
BackBlaze::EU_CENTRAL_001; // eu-central-001
BackBlaze::EU_CENTRAL_002; // eu-central-002
BackBlaze::EU_CENTRAL_003; // eu-central-003
BackBlaze::EU_CENTRAL_004; // eu-central-004use Cesurapp\StorageBundle\Storage\Storage;
class FileController
{
public function upload(Storage $storage): void
{
// Upload file (uses default device)
$storage->upload('/tmp/photo.jpg', 'users/123/photo.jpg');
// Write content directly
$storage->write('Hello World', 'documents/hello.txt', 'text/plain');
// Check if file exists
if ($storage->exists('users/123/photo.jpg')) {
// Get file URL
$url = $storage->getUrl('users/123/photo.jpg');
// Get file size
$size = $storage->getSize('users/123/photo.jpg');
// Get MIME type
$mime = $storage->getMimeType('users/123/photo.jpg');
}
// Download file content
$content = $storage->download('users/123/photo.jpg');
// Download as resource
$resource = $storage->downloadResource('users/123/photo.jpg');
// Download as chunks (cloud only)
foreach ($storage->downloadChunk('large-file.zip') as $chunk) {
echo $chunk;
}
// Delete file
$storage->delete('users/123/photo.jpg');
}
}// Use a specific storage device instead of default
$storage->device('local')->upload('/tmp/file.pdf', 'documents/file.pdf');
$storage->device('backblaze')->upload('/tmp/backup.zip', 'backups/backup.zip');$storage->upload('/tmp/photo.jpg', 'users/123/photo.jpg', [
'ContentType' => 'image/jpeg',
'CacheControl' => 'max-age=3600',
'Metadata' => [
'user-id' => '123',
'uploaded-by' => 'admin'
]
]);// Generate temporary access URL (expires in 1 hour)
$client = $storage->device('main')->getClient();
$presignedUrl = $client->getPresignedUrl(
'bucket-name',
'path/to/file.jpg',
new \DateTimeImmutable('+1 hour')
);$localDriver = $storage->device('local');
// Get file hash (MD5)
$hash = $localDriver->getFileHash('documents/file.pdf');
// Get directory size
$size = $localDriver->getDirectorySize('users/123/');
// Get partition free space
$freeSpace = $localDriver->getPartitionFreeSpace();
// Get partition total space
$totalSpace = $localDriver->getPartitionTotalSpace();
// Move file
$localDriver->move('/tmp/file.pdf', 'documents/moved-file.pdf');
// Delete directory
$localDriver->deletePath('temp/uploads/');Tests for cloud providers require access credentials. Tests without credentials are automatically skipped.
Edit phpunit.xml.dist:
<!-- BackBlaze Test Keys -->
<server name="BACKBLAZE_ACCESS_KEY" value="your_key_id"/>
<server name="BACKBLAZE_SECRET" value="your_application_key"/>
<server name="BACKBLAZE_BUCKET" value="your_test_bucket"/>
<!-- CloudFlare R2 Test Keys -->
<server name="CLOUDFLARE_R2_ACCESS_KEY" value="your_access_key"/>
<server name="CLOUDFLARE_R2_SECRET" value="your_secret_key"/>
<server name="CLOUDFLARE_R2_ENDPOINT" value="https://your-account-id.r2.cloudflarestorage.com"/>
<server name="CLOUDFLARE_R2_BUCKET" value="your_test_bucket"/>composer testFor detailed usage guidelines and best practices, see GUIDELINES.md
MIT License - see LICENSE for details