Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/firebase_image.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ library firebase_image;

export 'src/firebase_image.dart';
export 'src/cache_refresh_strategy.dart';
export 'src/image_fetch_strategy.dart';
34 changes: 29 additions & 5 deletions lib/src/cache_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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<void> open() async {
db = await openDatabase(
Expand Down Expand Up @@ -138,11 +143,30 @@ class FirebaseImageCacheManager {
return null;
}

Future<Uint8List> remoteFileBytes(
FirebaseImageObject object, int maxSizeBytes) {
Future<Uint8List> _fetchToMemory(FirebaseImageObject object, int maxSizeBytes) {
return object.reference.getData(maxSizeBytes);
}

Future<Uint8List> _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<Uint8List> 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<Uint8List> upsertRemoteFileToCache(
FirebaseImageObject object, int maxSizeBytes) async {
if (CacheRefreshStrategy.BY_METADATA_DATE == this.cacheRefreshStrategy) {
Expand Down
66 changes: 49 additions & 17 deletions lib/src/firebase_image.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<FirebaseImage> {
// Default: True. Specified whether or not an image should be cached (optional)
final bool shouldCache;
Expand All @@ -28,6 +33,14 @@ class FirebaseImage extends ImageProvider<FirebaseImage> {
/// 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
Expand All @@ -43,11 +56,17 @@ class FirebaseImage extends ImageProvider<FirebaseImage> {
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<Uint8List> getBytes() {
Expand All @@ -72,28 +91,34 @@ class FirebaseImage extends ImageProvider<FirebaseImage> {

Future<Uint8List> _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;
Expand All @@ -104,6 +129,13 @@ class FirebaseImage extends ImageProvider<FirebaseImage> {
.instantiateImageCodec(await _fetchImage());
}

Future<bool> delete() async {
await _cacheManager.open();
await _cacheManager.delete(_imageObject.uri);

return super.evict();
}

@override
Future<FirebaseImage> obtainKey(ImageConfiguration configuration) {
return SynchronousFuture<FirebaseImage>(this);
Expand Down
7 changes: 7 additions & 0 deletions lib/src/image_fetch_strategy.dart
Original file line number Diff line number Diff line change
@@ -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,
}