diff --git a/lib/firebase_image.dart b/lib/firebase_image.dart index 1b5b38c..f700db3 100644 --- a/lib/firebase_image.dart +++ b/lib/firebase_image.dart @@ -2,3 +2,4 @@ library firebase_image; export 'src/firebase_image.dart'; export 'src/cache_refresh_strategy.dart'; +export 'src/image_fetch_strategy.dart'; diff --git a/lib/src/cache_manager.dart b/lib/src/cache_manager.dart index 80bbc24..b52e18c 100644 --- a/lib/src/cache_manager.dart +++ b/lib/src/cache_manager.dart @@ -6,10 +6,13 @@ import 'package:firebase_image/src/firebase_image.dart'; import 'package:firebase_image/src/image_object.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_storage/firebase_storage.dart'; +import 'package:flutter/cupertino.dart'; import 'package:sqflite/sqflite.dart'; import 'package:path_provider/path_provider.dart'; import 'package:path/path.dart'; +import 'image_fetch_strategy.dart'; + class FirebaseImageCacheManager { static const String key = 'firebase_image'; @@ -19,10 +22,12 @@ class FirebaseImageCacheManager { String basePath; final CacheRefreshStrategy cacheRefreshStrategy; + final ImageFetchStrategy imageFetchStrategy; - FirebaseImageCacheManager( - this.cacheRefreshStrategy, - ); + FirebaseImageCacheManager({ + @required this.cacheRefreshStrategy, + this.imageFetchStrategy = ImageFetchStrategy.FETCH_TO_MEMORY, + }) : assert(cacheRefreshStrategy != null); Future open() async { db = await openDatabase( @@ -138,11 +143,30 @@ class FirebaseImageCacheManager { return null; } - Future remoteFileBytes( - FirebaseImageObject object, int maxSizeBytes) { + Future _fetchToMemory(FirebaseImageObject object, int maxSizeBytes) { return object.reference.getData(maxSizeBytes); } + Future _fetchToFile(FirebaseImageObject object) async { + Directory dir = await getApplicationSupportDirectory(); + File file = File('${dir.path}/${object.remotePath}'); + file.createSync(recursive: true); + DownloadTask task = object.reference.writeToFile(file); + await task; + return file.readAsBytesSync(); + } + + Future remoteFileBytes(FirebaseImageObject object, int maxSizeBytes) { + if (imageFetchStrategy == ImageFetchStrategy.FETCH_TO_FILE) { + return _fetchToFile(object); + } + else if (imageFetchStrategy == ImageFetchStrategy.FETCH_TO_MEMORY) { + return _fetchToMemory(object, maxSizeBytes); + } + + throw(Exception("ImageFetchStrategy missing")); + } + Future upsertRemoteFileToCache( FirebaseImageObject object, int maxSizeBytes) async { if (CacheRefreshStrategy.BY_METADATA_DATE == this.cacheRefreshStrategy) { diff --git a/lib/src/firebase_image.dart b/lib/src/firebase_image.dart index 1c22f03..48f0f76 100644 --- a/lib/src/firebase_image.dart +++ b/lib/src/firebase_image.dart @@ -9,6 +9,11 @@ import 'package:firebase_storage/firebase_storage.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'cache_manager.dart'; +import 'image_fetch_strategy.dart'; + +typedef FirebaseImageError = Uint8List Function(Exception exception); + class FirebaseImage extends ImageProvider { // Default: True. Specified whether or not an image should be cached (optional) final bool shouldCache; @@ -28,6 +33,14 @@ class FirebaseImage extends ImageProvider { /// The model for the image object final FirebaseImageObject _imageObject; + /// Default: FETCH_TO_MEMORY. Specifies the strategy in which to fetch the image from Firebase (optional) + final ImageFetchStrategy imageFetchStrategy; + + /// An optional response to when an error occurs (optional) + final FirebaseImageError onError; + + final FirebaseImageCacheManager _cacheManager; + /// Fetches, saves and returns an ImageProvider for any image in a readable Firebase Cloud Storeage bucket. /// /// [location] The URI of the image, in the bucket, to be displayed @@ -43,11 +56,17 @@ class FirebaseImage extends ImageProvider { this.maxSizeBytes = 2500 * 1000, // 2.5MB this.cacheRefreshStrategy = CacheRefreshStrategy.BY_METADATA_DATE, this.firebaseApp, + this.imageFetchStrategy = ImageFetchStrategy.FETCH_TO_MEMORY, + this.onError, }) : _imageObject = FirebaseImageObject( bucket: _getBucket(location), remotePath: _getImagePath(location), reference: _getImageRef(location, firebaseApp), - ); + ), + _cacheManager = FirebaseImageCacheManager( + cacheRefreshStrategy: cacheRefreshStrategy, + imageFetchStrategy: imageFetchStrategy, + ); /// Returns the image as bytes Future getBytes() { @@ -72,28 +91,34 @@ class FirebaseImage extends ImageProvider { Future _fetchImage() async { Uint8List bytes; - FirebaseImageCacheManager cacheManager = FirebaseImageCacheManager( - cacheRefreshStrategy, - ); - if (shouldCache) { - await cacheManager.open(); - FirebaseImageObject localObject = - await cacheManager.get(_imageObject.uri, this); + try { + if (shouldCache) { + await _cacheManager.open(); + FirebaseImageObject localObject = + await _cacheManager.get(_imageObject.uri, this); - if (localObject != null) { - bytes = await cacheManager.localFileBytes(localObject); - if (bytes == null) { - bytes = await cacheManager.upsertRemoteFileToCache( + if (localObject != null) { + bytes = await _cacheManager.localFileBytes(localObject); + if (bytes == null) { + bytes = await _cacheManager.upsertRemoteFileToCache( _imageObject, this.maxSizeBytes); + } + } else { + bytes = await _cacheManager.upsertRemoteFileToCache( + _imageObject, this.maxSizeBytes); } } else { - bytes = await cacheManager.upsertRemoteFileToCache( - _imageObject, this.maxSizeBytes); + bytes = + await _cacheManager.remoteFileBytes(_imageObject, this.maxSizeBytes); + } + } + catch (ex) { + await delete(); + + if (this.onError != null) { + bytes = this.onError(ex); } - } else { - bytes = - await cacheManager.remoteFileBytes(_imageObject, this.maxSizeBytes); } return bytes; @@ -104,6 +129,13 @@ class FirebaseImage extends ImageProvider { .instantiateImageCodec(await _fetchImage()); } + Future delete() async { + await _cacheManager.open(); + await _cacheManager.delete(_imageObject.uri); + + return super.evict(); + } + @override Future obtainKey(ImageConfiguration configuration) { return SynchronousFuture(this); diff --git a/lib/src/image_fetch_strategy.dart b/lib/src/image_fetch_strategy.dart new file mode 100644 index 0000000..7e7d993 --- /dev/null +++ b/lib/src/image_fetch_strategy.dart @@ -0,0 +1,7 @@ +enum ImageFetchStrategy { + // Asynchronously downloads the object at the StorageReference to a list in memory. + // A list of the provided max size will be allocated. + FETCH_TO_MEMORY, + // Asynchronously downloads the object to a specified system file. + FETCH_TO_FILE, +} \ No newline at end of file