Skip to content
Open
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
94 changes: 67 additions & 27 deletions cached_network_image/lib/src/image_provider/_image_loader.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'dart:ui';
Expand Down Expand Up @@ -93,36 +94,56 @@ class ImageLoader implements platform.ImageLoader {
'CacheManager needs to be an ImageCacheManager. maxWidth and '
'maxHeight will be ignored when a normal CacheManager is used.');

final stream = cacheManager is ImageCacheManager
? cacheManager.getImageFile(
url,
maxHeight: maxHeight,
maxWidth: maxWidth,
withProgress: true,
headers: headers,
key: cacheKey,
)
: cacheManager.getFileStream(
url,
withProgress: true,
headers: headers,
key: cacheKey,
for (int attempt = 0; attempt < 2; attempt++) {
final stream = cacheManager is ImageCacheManager
? cacheManager.getImageFile(
url,
maxHeight: maxHeight,
maxWidth: maxWidth,
withProgress: true,
headers: headers,
key: cacheKey,
)
: cacheManager.getFileStream(
url,
withProgress: true,
headers: headers,
key: cacheKey,
);

await for (final result in stream) {
if (result is DownloadProgress) {
chunkEvents.add(
ImageChunkEvent(
cumulativeBytesLoaded: result.downloaded,
expectedTotalBytes: result.totalSize,
),
);
bool shouldRetry = false;
await for (final result in stream) {
if (result is DownloadProgress) {
chunkEvents.add(
ImageChunkEvent(
cumulativeBytesLoaded: result.downloaded,
expectedTotalBytes: result.totalSize,
),
);
}
if (result is FileInfo) {
final file = result.file;
try {
final bytes = await file.readAsBytes();
final decoded = await decode(bytes);
yield decoded;
return;
} on FileSystemException catch (error) {
if (_isFileMissing(error) && attempt == 0) {
shouldRetry = true;
await _evictMissingCacheEntry(
cacheManager,
cacheKey ?? url,
evictImage,
);
break;
}
rethrow;
}
}
}
if (result is FileInfo) {
final file = result.file;
final bytes = await file.readAsBytes();
final decoded = await decode(bytes);
yield decoded;
if (!shouldRetry) {
return;
}
}
} on Object catch (error, stackTrace) {
Expand All @@ -137,4 +158,23 @@ class ImageLoader implements platform.ImageLoader {
await chunkEvents.close();
}
}

bool _isFileMissing(FileSystemException error) {
return error is PathNotFoundException || error.osError?.errorCode == 2;
}

Future<void> _evictMissingCacheEntry(
BaseCacheManager cacheManager,
String key,
VoidCallback evictImage,
) async {
try {
await cacheManager.removeFile(key);
} catch (_) {
// Ignore cache eviction failures; we'll surface the original error if needed.
}
scheduleMicrotask(() {
evictImage();
});
}
}