# FileSystem Package Specification ## Overview The FileSystem package provides a robust abstraction layer for file operations, matching Laravel's filesystem functionality. It supports local and cloud storage systems through a unified API, with support for streaming, visibility control, and metadata management. > **Related Documentation** > - See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) for implementation status > - See [Foundation Integration Guide](foundation_integration_guide.md) for integration patterns > - See [Testing Guide](testing_guide.md) for testing approaches > - See [Getting Started Guide](getting_started.md) for development setup > - See [Contracts Package Specification](contracts_package_specification.md) for filesystem contracts ## Core Features ### 1. Filesystem Manager ```dart /// Manages filesystem drivers class FilesystemManager implements FilesystemFactory { /// Available filesystem drivers final Map _drivers = {}; /// Default driver name final String _defaultDriver; /// Configuration repository final ConfigContract _config; FilesystemManager(this._config) : _defaultDriver = _config.get('filesystems.default', 'local'); @override Filesystem disk([String? name]) { name ??= _defaultDriver; return _drivers.putIfAbsent(name, () { var config = _getConfig(name!); var driver = _createDriver(config); return Filesystem(driver); }); } /// Creates a driver instance FilesystemDriver _createDriver(Map config) { switch (config['driver']) { case 'local': return LocalDriver(config); case 's3': return S3Driver(config); case 'gcs': return GoogleCloudDriver(config); default: throw UnsupportedError( 'Unsupported filesystem driver: ${config['driver']}' ); } } /// Gets configuration for driver Map _getConfig(String name) { var config = _config.get('filesystems.disks.$name'); if (config == null) { throw ArgumentError('Disk [$name] not configured.'); } return config; } } ``` ### 2. Filesystem Implementation ```dart /// Core filesystem implementation class Filesystem implements FilesystemContract { /// The filesystem driver final FilesystemDriver _driver; Filesystem(this._driver); @override Future exists(String path) { return _driver.exists(path); } @override Future get(String path) { return _driver.get(path); } @override Stream> readStream(String path) { return _driver.readStream(path); } @override Future put(String path, dynamic contents, [Map? options]) { return _driver.put(path, contents, options); } @override Future putStream(String path, Stream> contents, [Map? options]) { return _driver.putStream(path, contents, options); } @override Future delete(String path) { return _driver.delete(path); } @override Future copy(String from, String to) { return _driver.copy(from, to); } @override Future move(String from, String to) { return _driver.move(from, to); } @override Future url(String path) { return _driver.url(path); } @override Future> metadata(String path) { return _driver.metadata(path); } @override Future size(String path) { return _driver.size(path); } @override Future mimeType(String path) { return _driver.mimeType(path); } @override Future lastModified(String path) { return _driver.lastModified(path); } } ``` ### 3. Local Driver ```dart /// Local filesystem driver class LocalDriver implements FilesystemDriver { /// Root path for local filesystem final String _root; /// Default visibility final String _visibility; LocalDriver(Map config) : _root = config['root'], _visibility = config['visibility'] ?? 'private'; @override Future exists(String path) async { return File(_fullPath(path)).exists(); } @override Future get(String path) async { return File(_fullPath(path)).readAsString(); } @override Stream> readStream(String path) { return File(_fullPath(path)).openRead(); } @override Future put(String path, dynamic contents, [Map? options]) async { var file = File(_fullPath(path)); await file.create(recursive: true); if (contents is String) { await file.writeAsString(contents); } else if (contents is List) { await file.writeAsBytes(contents); } else { throw ArgumentError('Invalid content type'); } await _setVisibility(file, options?['visibility'] ?? _visibility); } @override Future putStream(String path, Stream> contents, [Map? options]) async { var file = File(_fullPath(path)); await file.create(recursive: true); var sink = file.openWrite(); await contents.pipe(sink); await sink.close(); await _setVisibility(file, options?['visibility'] ?? _visibility); } /// Gets full path for file String _fullPath(String path) { return p.join(_root, path); } /// Sets file visibility Future _setVisibility(File file, String visibility) async { // Set file permissions based on visibility if (visibility == 'public') { await file.setPermissions( unix: 0644, windows: FilePermissions.readWrite ); } else { await file.setPermissions( unix: 0600, windows: FilePermissions.readWriteExecute ); } } } ``` ### 4. Cloud Drivers ```dart /// Amazon S3 driver class S3Driver implements FilesystemDriver { /// S3 client final S3Client _client; /// Bucket name final String _bucket; /// Optional path prefix final String? _prefix; S3Driver(Map config) : _client = S3Client( region: config['region'], credentials: AWSCredentials( accessKey: config['key'], secretKey: config['secret'] ) ), _bucket = config['bucket'], _prefix = config['prefix']; @override Future exists(String path) async { try { await _client.headObject( bucket: _bucket, key: _prefixPath(path) ); return true; } catch (e) { return false; } } @override Future put(String path, dynamic contents, [Map? options]) async { await _client.putObject( bucket: _bucket, key: _prefixPath(path), body: contents, acl: options?['visibility'] == 'public' ? 'public-read' : 'private' ); } /// Adds prefix to path String _prefixPath(String path) { return _prefix != null ? '$_prefix/$path' : path; } } /// Google Cloud Storage driver class GoogleCloudDriver implements FilesystemDriver { /// Storage client final Storage _storage; /// Bucket name final String _bucket; GoogleCloudDriver(Map config) : _storage = Storage( projectId: config['project_id'], credentials: config['credentials'] ), _bucket = config['bucket']; @override Future exists(String path) async { try { await _storage.bucket(_bucket).file(path).exists(); return true; } catch (e) { return false; } } @override Future put(String path, dynamic contents, [Map? options]) async { var file = _storage.bucket(_bucket).file(path); if (contents is String) { await file.writeAsString(contents); } else if (contents is List) { await file.writeAsBytes(contents); } else { throw ArgumentError('Invalid content type'); } if (options?['visibility'] == 'public') { await file.makePublic(); } } } ``` ## Integration with Container ```dart /// Registers filesystem services class FilesystemServiceProvider extends ServiceProvider { @override void register() { // Register filesystem factory container.singleton((c) { return FilesystemManager(c.make()); }); // Register default filesystem container.singleton((c) { return c.make().disk(); }); } } ``` ## Usage Examples ### Basic File Operations ```dart // Get default disk var storage = Storage.disk(); // Check if file exists if (await storage.exists('file.txt')) { // Read file contents var contents = await storage.get('file.txt'); // Write file contents await storage.put('new-file.txt', contents); // Delete file await storage.delete('file.txt'); } ``` ### Stream Operations ```dart // Read file as stream var stream = storage.readStream('large-file.txt'); // Write stream to file await storage.putStream( 'output.txt', stream, {'visibility': 'public'} ); ``` ### Cloud Storage ```dart // Use S3 disk var s3 = Storage.disk('s3'); // Upload file await s3.put( 'uploads/image.jpg', imageBytes, {'visibility': 'public'} ); // Get public URL var url = await s3.url('uploads/image.jpg'); ``` ### File Metadata ```dart // Get file metadata var meta = await storage.metadata('document.pdf'); print('Size: ${meta['size']}'); print('Type: ${meta['mime_type']}'); print('Modified: ${meta['last_modified']}'); ``` ## Testing ```dart void main() { group('Filesystem Tests', () { late Filesystem storage; setUp(() { storage = Filesystem(MockDriver()); }); test('should check file existence', () async { expect(await storage.exists('test.txt'), isTrue); expect(await storage.exists('missing.txt'), isFalse); }); test('should read and write files', () async { await storage.put('test.txt', 'contents'); var contents = await storage.get('test.txt'); expect(contents, equals('contents')); }); test('should handle streams', () async { var input = Stream.fromIterable([ [1, 2, 3], [4, 5, 6] ]); await storage.putStream('test.bin', input); var output = storage.readStream('test.bin'); expect( await output.toList(), equals([[1, 2, 3], [4, 5, 6]]) ); }); }); } ``` ## Performance Considerations 1. **Streaming Large Files** ```dart // Use streams for large files class Filesystem { Future copyLarge(String from, String to) async { await readStream(from) .pipe(writeStream(to)); } } ``` 2. **Caching URLs** ```dart class CachingFilesystem implements FilesystemContract { final Cache _cache; final Duration _ttl; @override Future url(String path) async { var key = 'file_url:$path'; return _cache.remember(key, _ttl, () { return _driver.url(path); }); } } ``` 3. **Batch Operations** ```dart class Filesystem { Future putMany(Map files) async { await Future.wait( files.entries.map((e) => put(e.key, e.value) ) ); } } ``` ## Next Steps 1. Implement core filesystem 2. Add local driver 3. Add cloud drivers 4. Create manager 5. Write tests 6. Add benchmarks Would you like me to focus on implementing any specific part of these packages or continue with other documentation? ## Development Guidelines ### 1. Getting Started Before implementing filesystem features: 1. Review [Getting Started Guide](getting_started.md) 2. Check [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) 3. Follow [Testing Guide](testing_guide.md) 4. Use [Foundation Integration Guide](foundation_integration_guide.md) 5. Understand [Contracts Package Specification](contracts_package_specification.md) ### 2. Implementation Process For each filesystem feature: 1. Write tests following [Testing Guide](testing_guide.md) 2. Implement following Laravel patterns 3. Document following [Getting Started Guide](getting_started.md#documentation) 4. Integrate following [Foundation Integration Guide](foundation_integration_guide.md) ### 3. Quality Requirements All implementations must: 1. Pass all tests (see [Testing Guide](testing_guide.md)) 2. Meet Laravel compatibility requirements 3. Follow integration patterns (see [Foundation Integration Guide](foundation_integration_guide.md)) 4. Implement required contracts (see [Contracts Package Specification](contracts_package_specification.md)) ### 4. Integration Considerations When implementing filesystem features: 1. Follow patterns in [Foundation Integration Guide](foundation_integration_guide.md) 2. Ensure Laravel compatibility per [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) 3. Use testing approaches from [Testing Guide](testing_guide.md) 4. Follow development setup in [Getting Started Guide](getting_started.md) 5. Implement all contracts from [Contracts Package Specification](contracts_package_specification.md) ### 5. Performance Guidelines Filesystem system must: 1. Handle large files efficiently 2. Use streaming where appropriate 3. Minimize memory usage 4. Support concurrent operations 5. Meet performance targets in [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md#performance-benchmarks) ### 6. Testing Requirements Filesystem tests must: 1. Cover all file operations 2. Test streaming behavior 3. Verify cloud storage 4. Check metadata handling 5. Follow patterns in [Testing Guide](testing_guide.md) ### 7. Documentation Requirements Filesystem documentation must: 1. Explain filesystem patterns 2. Show driver examples 3. Cover error handling 4. Include performance tips 5. Follow standards in [Getting Started Guide](getting_started.md#documentation)