x_storage_core 0.2.2
x_storage_core: ^0.2.2 copied to clipboard
Core package for x_storage providing XStorageProvider and other base functionality
x_storage_core #
XStorage is a Flutter package that provides a unified interface for handling different storage services (Firebase Storage, local file system, AWS S3, etc.). This package provides the core functionality of XStorage.
Why XStorage? #
File storage management in app development can be complex and time-consuming. Some common challenges include:
- Using multiple storage services: Having to use different APIs when you need both local and cloud storage in your app
- Switching storage providers: Need for extensive code changes when changing storage providers during development or production
- Testing difficulties: Complexity in creating mocks and building test environments
- Error handling: Implementing consistent error handling across different storage services
XStorage solves these challenges by providing a unified interface. It abstracts the type of storage and allows file operations through a consistent URI-based API.
Key Features #
- Unified storage interface: Operate different storage services through the same API via
XStorageProvider - URI-based file management: Handle files with a consistent path format regardless of storage type using
XUri - Multiple provider support: Register and use multiple storage services simultaneously
- Basic file operations: Support for read, write, delete, existence check, and other basic operations
- Robust error handling: Safe error handling using the
Resulttype - Extensibility: Easy creation of custom storage providers
- File transfer between providers: Support for transferring files between different storage services
Understanding XUri #
XUri is a core concept in XStorage that provides a unified way to reference files across different storage providers. It consists of:
scheme://path/to/file.ext
Where:
- scheme: Identifies the storage provider (e.g., 'file', 'firebase', 's3')
- path: The path to the file within that storage system
Examples:
// Local file reference
final localUri = XUri.create('file', 'documents/report.pdf');
// Firebase Storage reference
final firebaseUri = XUri.create('firebase', 'user_uploads/profile.jpg');
// S3 reference (using a presigned URL provider)
final s3Uri = XUri.create('s3', 'bucket/images/banner.png');
XUri allows your application to reference files in a storage-agnostic way. This means you can change storage providers without changing file references throughout your code.
Available Storage Providers #
The XStorage ecosystem offers these storage providers:
- FileStorageProvider: For local file system (
x_storage_filepackage) - FirebaseStorageProvider: For Firebase Storage (
x_storage_firebasepackage) - PresignedUrlStorageProvider: For storage services that use presigned URLs like AWS S3 (
x_storage_presigned_urlpackage) - AssetStorageProvider: For Flutter assets (included in this package)
Getting Started #
Installation #
Add dependencies to your pubspec.yaml:
dependencies:
x_storage_core: ^0.0.1
# Add additional provider packages as needed
x_storage_file: ^0.0.1 # For file system
x_storage_firebase: ^0.0.1 # For Firebase Storage
x_storage_presigned_url: ^0.0.1 # For presigned URL storage
Basic Usage #
import 'package:x_storage_core/x_storage_core.dart';
import 'package:x_storage_file/x_storage_file.dart';
import 'package:x_storage_firebase/x_storage_firebase.dart';
void main() async {
// Create XStorage instance
final storage = XStorage();
// Register providers
storage.registerProvider(FileStorageProvider());
storage.registerProvider(FirebaseStorageProvider(
firebaseStorage: FirebaseStorage.instance,
));
// Save a file (example: saving to local storage)
final saveResult = await storage.saveFile(
XUri.create('file', 'documents/myfile.txt'),
Uint8List.fromList(utf8.encode('Hello, World!')),
);
// Error handling with Result type
if (saveResult.isSuccess) {
print('File saved successfully');
} else {
print('Error: ${saveResult.failure.message}');
}
// Load a file
final loadResult = await storage.loadFile(
XUri.create('file', 'documents/myfile.txt'),
);
if (loadResult.isSuccess) {
final content = utf8.decode(loadResult.success);
print('File content: $content');
} else {
print('Load error: ${loadResult.failure.message}');
}
// Check if file exists
final exists = await storage.exists(
XUri.create('file', 'documents/myfile.txt'),
);
print('Does file exist? $exists');
// Delete a file
final deleteResult = await storage.deleteFile(
XUri.create('file', 'documents/myfile.txt'),
);
if (deleteResult.isSuccess) {
print('File deleted');
} else {
print('Delete error: ${deleteResult.failure.message}');
}
}
Transferring Files Between Storage Providers #
XStorage makes it easy to transfer files between different storage services:
// Example: Download from Firebase Storage to local storage
Future<void> downloadFromFirebaseToLocal() async {
final storage = XStorage();
// Register both providers
storage.registerProvider(FileStorageProvider());
storage.registerProvider(FirebaseStorageProvider(
firebaseStorage: FirebaseStorage.instance,
));
// Download file from Firebase URL
final downloadResult = await storage.downloadFile(
XUri.create('firebase', 'images/photo.jpg'),
onProgress: (received, total) {
final progress = (received / total * 100).toStringAsFixed(2);
print('Download progress: $progress%');
},
);
if (downloadResult.isSuccess) {
final localUri = downloadResult.success;
print('File saved locally: ${localUri.toString()}');
} else {
print('Download error: ${downloadResult.failure.message}');
}
}
Creating Custom Storage Providers #
To support a new storage service, extend XStorageProvider and implement the required methods:
class CustomStorageProvider extends XStorageProvider with NetworkProviderMixin {
@override
String get scheme => 'custom';
@override
String get rootUrl => 'https://custom-storage.example.com';
@override
Future<Result<void, XStorageException>> saveFile(XUri uri, Uint8List data) async {
try {
// Implementation for uploading to your custom storage service
// ...
return Result.success(null);
} catch (e) {
return Result.failure(UnknownException(e));
}
}
@override
Future<Result<Uint8List, XStorageException>> loadFile(XUri uri) async {
try {
// Implementation for downloading from your custom storage service
// ...
if (/* file found */) {
return Result.success(data);
} else {
return Result.failure(FileNotFoundException(uri));
}
} catch (e) {
return Result.failure(UnknownException(e));
}
}
@override
Future<Result<void, XStorageException>> deleteFile(XUri uri) async {
try {
// Implementation for deleting from your custom storage service
// ...
return Result.success(null);
} catch (e) {
return Result.failure(UnknownException(e));
}
}
@override
Future<bool> exists(XUri uri) async {
try {
// Implementation for checking file existence
// ...
return /* boolean indicating existence */;
} catch (e) {
return false;
}
}
}
Available Mixins and Their Benefits #
XStorage provides mixins to facilitate implementation of different storage service types:
-
NetworkProviderMixin: For network-based storage
- Provides conversion from URI to network URL through the
getNetworkUrlmethod - Ideal for implementing cloud storage services (Firebase, S3, etc.)
- Provides conversion from URI to network URL through the
-
FileProviderMixin: For file system-based storage
- Provides conversion from URI to local file path through the
getFilePathmethod - Ideal for implementing local storage in your app
- Provides conversion from URI to local file path through the
-
AssetProviderMixin: For Flutter assets
- Provides functionality to access assets in Flutter apps
- Ideal for loading resources bundled with the application
Error Handling #
XStorage uses the Result<T, E> type for concise and safe error handling:
// Example of file loading
final result = await storage.loadFile(XUri.create('file', 'document.txt'));
// Error handling using switch expression
switch (result) {
case Success(value: final data):
final text = utf8.decode(data);
print('File content: $text');
break;
case Failure(value: final error):
switch (error) {
case FileNotFoundException():
print('File not found');
break;
case AccessDeniedException():
print('Access denied');
break;
default:
print('An error occurred: ${error.message}');
break;
}
break;
}
License #
This project is licensed under the MIT License - see the LICENSE file for details.