Storagefs
A powerful and flexible filesystem abstraction library for Dart, inspired by Laravel's Storage facade. Provides a unified API for working with local filesystems and cloud storage services.
Features
- ๐๏ธ Unified API - Same interface for local and cloud storage
- โ๏ธ Cloud Storage - Full support for S3-compatible services (AWS S3, Cloudflare R2, MinIO, etc.)
- ๐ Signed URLs - Generate secure temporary URLs for cloud storage
- ๐๏ธ File Visibility - Control public/private access with ACL support
- โก Async-First - Built for efficient non-blocking I/O operations
- ๐ง Type-Safe - Strongly typed configuration and APIs
- ๐งช Testing Support - Built-in fake disks for easy testing
- ๐ฆ Laravel-Inspired - Familiar API for PHP/Laravel developers
- ๐ฏ Multiple Disks - Configure and switch between multiple storage backends
Installation
Add this to your pubspec.yaml:
dependencies:
storage_fs: ^0.1.0
Quick Start
import 'package:storage_fs/storage_fs.dart';
void main() async {
// Initialize the storage system
Storage.initialize({
'default': 'local',
'disks': {
'local': {
'driver': 'local',
'root': './storage',
},
},
});
// Write a file
await Storage.put('hello.txt', 'Hello, World!');
// Read a file
final content = await Storage.get('hello.txt');
print(content); // Hello, World!
// Check if file exists
if (await Storage.exists('hello.txt')) {
print('File exists!');
}
// Delete a file
await Storage.delete('hello.txt');
}
Configuration
Local Filesystem
Storage.initialize({
'default': 'local',
'disks': {
'local': {
'driver': 'local',
'root': './storage',
'throw': false, // Whether to throw exceptions or return false
},
},
});
Cloud Storage (S3-Compatible)
Storage.initialize({
'default': 's3',
'cloud': 's3',
'disks': {
's3': {
'driver': 's3',
'options': {
'endpoint': 'your-endpoint.r2.cloudflarestorage.com',
'key': 'your-access-key-id',
'secret': 'your-secret-access-key',
'bucket': 'your-bucket-name',
'use_ssl': true,
'region': 'auto', // Optional
},
},
},
});
Multiple Disks
Storage.initialize({
'default': 'local',
'cloud': 's3',
'disks': {
'local': {
'driver': 'local',
'root': './storage',
},
'public': {
'driver': 'local',
'root': './public',
},
's3': {
'driver': 's3',
'options': {
'endpoint': 's3.amazonaws.com',
'key': 'your-key',
'secret': 'your-secret',
'bucket': 'my-bucket',
'use_ssl': true,
},
},
},
});
Usage
Basic File Operations
// Write files
await Storage.put('file.txt', 'Hello World');
await Storage.put('data.bin', <int>[1, 2, 3, 4]);
// Read files
final content = await Storage.get('file.txt');
print(content);
// Read as stream (for large files)
final stream = Storage.readStream('large-file.bin');
await for (final chunk in stream!) {
// Process chunk
}
// Check if file exists
if (await Storage.exists('file.txt')) {
print('File exists!');
}
// Check if file is missing
if (await Storage.missing('file.txt')) {
print('File not found');
}
// Delete files
await Storage.delete('file.txt');
await Storage.delete(['file1.txt', 'file2.txt', 'file3.txt']);
File Metadata
// Get file size
final size = await Storage.size('file.txt');
print('Size: $size bytes');
// Get last modified time
final modified = await Storage.lastModified('file.txt');
print('Modified: $modified');
// Get MIME type
final mimeType = await Storage.mimeType('image.jpg');
print('MIME type: $mimeType'); // image/jpeg
// Get checksum
final checksum = await Storage.checksum('file.txt', algorithm: 'md5');
print('MD5: $checksum');
Copy and Move Operations
// Copy a file
await Storage.copy('original.txt', 'copy.txt');
// Move/rename a file
await Storage.move('old-name.txt', 'new-name.txt');
// Upload local file
final path = await Storage.putFile('uploads', File('/tmp/photo.jpg'));
print('Uploaded to: $path'); // uploads/photo.jpg
// Upload with custom name
final customPath = await Storage.putFileAs(
'uploads',
File('/tmp/photo.jpg'),
'avatar.jpg',
);
print('Uploaded to: $customPath'); // uploads/avatar.jpg
Append and Prepend
// Append to file
await Storage.put('log.txt', 'Initial log entry');
await Storage.append('log.txt', 'Another log entry', separator: '\n');
// Prepend to file
await Storage.prepend('log.txt', 'First entry', separator: '\n');
Directory Operations
// Create a directory
await Storage.makeDirectory('uploads/images');
// List files in directory
final files = await Storage.files('uploads');
for (final file in files) {
print(file);
}
// List files recursively
final allFiles = await Storage.allFiles('uploads');
// List directories
final dirs = await Storage.directories('uploads');
// List directories recursively
final allDirs = await Storage.allDirectories('uploads');
// Delete directory
await Storage.deleteDirectory('temp');
Working with Multiple Disks
// Use default disk
await Storage.put('file.txt', 'content');
// Use specific disk
final s3 = Storage.disk('s3');
await s3.put('file.txt', 'cloud content');
final local = Storage.disk('local');
await local.put('file.txt', 'local content');
// Use cloud disk directly
final cloud = Storage.cloud();
await cloud.put('file.txt', 'cloud content');
Cloud Storage - Signed URLs
Generate secure, time-limited URLs for accessing private files:
// Check if disk supports temporary URLs
if (Storage.providesTemporaryUrls()) {
// Generate temporary download URL (expires in 1 hour)
final downloadUrl = await Storage.getTemporaryUrl(
'private/document.pdf',
DateTime.now().add(Duration(hours: 1)),
);
print('Download URL: $downloadUrl');
// Generate temporary upload URL (expires in 30 minutes)
final uploadData = await Storage.getTemporaryUploadUrl(
'uploads/new-file.jpg',
DateTime.now().add(Duration(minutes: 30)),
);
final url = uploadData['url'] as String;
final headers = uploadData['headers'] as Map<String, String>;
print('Upload to: $url');
print('With headers: $headers');
}
File Visibility (ACL)
Control whether files are publicly accessible:
// Set file as public
await Storage.setVisibility('public-file.jpg', 'public');
// Set file as private
await Storage.setVisibility('private-data.pdf', 'private');
// Get current visibility
final visibility = await Storage.getVisibility('file.txt');
print(visibility); // 'public' or 'private'
// Get public URL
final url = Storage.url('public-file.jpg');
print(url);
Streaming Operations
For large files, use streaming to avoid memory issues:
// Write using stream
final inputStream = File('large-file.bin').openRead();
await Storage.writeStream('uploads/large-file.bin', inputStream);
// Read as stream
final outputStream = Storage.readStream('uploads/large-file.bin');
final file = File('downloaded.bin').openWrite();
await for (final chunk in outputStream!) {
file.add(chunk);
}
await file.close();
Advanced Features
Custom Temporary URL Builders
Customize how temporary URLs are generated:
Storage.buildTemporaryUrlsUsing((path, expiration, options) async {
// Your custom logic here
return 'https://custom.url/$path?expires=${expiration.millisecondsSinceEpoch}';
});
// Generate URL using custom builder
final url = await Storage.getTemporaryUrl('file.txt', DateTime.now().add(Duration(hours: 1)));
Reading JSON Files
// Read and parse JSON file
final data = await Storage.json('config.json');
print(data['version']);
Error Handling
try {
await Storage.put('file.txt', 'content');
} catch (e) {
print('Error: $e');
}
// Or configure to return false instead of throwing
Storage.initialize({
'default': 'local',
'disks': {
'local': {
'driver': 'local',
'root': './storage',
'throw': false, // Returns false on error instead of throwing
},
},
});
final success = await Storage.put('file.txt', 'content');
if (!success) {
print('Failed to write file');
}
Supported Storage Backends
Local Filesystem
Uses Dart's native file package for local filesystem operations.
Features:
- Full read/write access
- Directory operations
- Metadata support
- Fast and efficient
S3-Compatible Cloud Storage
Works with any S3-compatible storage service:
Supported Services:
- Amazon S3
- Cloudflare R2
- MinIO
- DigitalOcean Spaces
- Wasabi
- Backblaze B2 (S3-compatible API)
Features:
- Signed URLs (presigned URLs)
- Public/private ACL
- Metadata support
- Streaming uploads/downloads
Examples
Check the example/ directory for complete examples:
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
- Inspired by Laravel's Storage facade
- Built on top of the file package
- Uses minio for S3-compatible storage
Support
- ๐ API Documentation
- ๐ Issue Tracker
- ๐ฌ Discussions
Made with โค๏ธ for the Dart community
Libraries
- storage_fs
- A filesystem abstraction library for Dart, inspired by Laravel's Storage facade.