Добавил отправку файлов ! ! !

This commit is contained in:
jganenok
2025-11-22 12:08:57 +07:00
parent 4e81b607fa
commit 95b67c7891
6 changed files with 353 additions and 151 deletions

View File

@@ -5,7 +5,7 @@ import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as path;
import 'package:http/http.dart' as http;
import 'package:cached_network_image/cached_network_image.dart';
import 'package:es_compression/lz4.dart';
class ImageCacheService {
ImageCacheService._privateConstructor();
@@ -18,6 +18,8 @@ class ImageCacheService {
); // Кеш изображений на 7 дней
late Directory _cacheDirectory;
// LZ4 сжатие для экономии места
final Lz4Codec _lz4Codec = Lz4Codec();
Future<void> initialize() async {
final appDir = await getApplicationDocumentsDirectory();
@@ -27,30 +29,25 @@ class ImageCacheService {
await _cacheDirectory.create(recursive: true);
}
await _cleanupExpiredCache();
}
String getCachedImagePath(String url) {
final fileName = _generateFileName(url);
return path.join(_cacheDirectory.path, fileName);
}
bool isImageCached(String url) {
final file = File(getCachedImagePath(url));
return file.existsSync();
}
Future<File?> loadImage(String url, {bool forceRefresh = false}) async {
if (!forceRefresh && isImageCached(url)) {
final cachedFile = File(getCachedImagePath(url));
if (await _isFileValid(cachedFile)) {
return cachedFile;
} else {
await cachedFile.delete();
}
}
@@ -59,8 +56,9 @@ class ImageCacheService {
final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
final file = File(getCachedImagePath(url));
await file.writeAsBytes(response.bodyBytes);
// Сжимаем данные перед сохранением
final compressedData = _lz4Codec.encode(response.bodyBytes);
await file.writeAsBytes(compressedData);
await _updateFileAccessTime(file);
@@ -73,38 +71,44 @@ class ImageCacheService {
return null;
}
Future<Uint8List?> loadImageAsBytes(
String url, {
bool forceRefresh = false,
}) async {
final file = await loadImage(url, forceRefresh: forceRefresh);
if (file != null) {
return await file.readAsBytes();
final compressedData = await file.readAsBytes();
try {
// Декомпрессируем данные
final decompressedData = _lz4Codec.decode(compressedData);
return Uint8List.fromList(decompressedData);
} catch (e) {
// Если декомпрессия не удалась, возможно файл не сжат (старый формат)
print(
'Ошибка декомпрессии изображения $url, пробуем прочитать как обычный файл: $e',
);
return compressedData;
}
}
return null;
}
Future<void> preloadImage(String url) async {
await loadImage(url);
}
Future<void> preloadContactAvatar(String? photoUrl) async {
if (photoUrl != null && photoUrl.isNotEmpty) {
await preloadImage(photoUrl);
}
}
Future<void> preloadProfileAvatar(String? photoUrl) async {
if (photoUrl != null && photoUrl.isNotEmpty) {
await preloadImage(photoUrl);
}
}
Future<void> preloadContactAvatars(List<String?> photoUrls) async {
final futures = photoUrls
.where((url) => url != null && url.isNotEmpty)
@@ -116,14 +120,46 @@ class ImageCacheService {
}
}
Future<void> clearCache() async {
if (_cacheDirectory.existsSync()) {
await _cacheDirectory.delete(recursive: true);
await _cacheDirectory.create(recursive: true);
await _clearDirectoryContents(_cacheDirectory);
}
}
Future<void> _clearDirectoryContents(Directory directory) async {
try {
// Очищаем содержимое директории, удаляя файлы по одному
await for (final entity in directory.list(recursive: true)) {
if (entity is File) {
try {
await entity.delete();
// Небольшая задержка между удалениями для избежания конфликтов
await Future.delayed(const Duration(milliseconds: 5));
} catch (fileError) {
// Игнорируем ошибки удаления отдельных файлов
print('Не удалось удалить файл ${entity.path}: $fileError');
}
} else if (entity is Directory) {
try {
// Рекурсивно очищаем поддиректории
await _clearDirectoryContents(entity);
try {
await entity.delete();
} catch (dirError) {
print(
'Не удалось удалить поддиректорию ${entity.path}: $dirError',
);
}
} catch (subDirError) {
print('Ошибка очистки поддиректории ${entity.path}: $subDirError');
}
}
}
print('Содержимое директории ${directory.path} очищено');
} catch (e) {
print('Ошибка очистки содержимого директории ${directory.path}: $e');
}
}
Future<int> getCacheSize() async {
int totalSize = 0;
@@ -137,7 +173,6 @@ class ImageCacheService {
return totalSize;
}
Future<int> getCacheFileCount() async {
int count = 0;
if (_cacheDirectory.existsSync()) {
@@ -150,7 +185,6 @@ class ImageCacheService {
return count;
}
Future<void> _cleanupExpiredCache() async {
if (!_cacheDirectory.existsSync()) return;
@@ -161,18 +195,15 @@ class ImageCacheService {
}
}
Future<bool> _isFileValid(File file) async {
if (!file.existsSync()) return false;
final stat = await file.stat();
final age = DateTime.now().difference(stat.modified);
return age < _cacheExpiration;
}
Future<bool> _isFileExpired(File file) async {
if (!file.existsSync()) return false;
@@ -182,19 +213,13 @@ class ImageCacheService {
return age >= _cacheExpiration;
}
Future<void> _updateFileAccessTime(File file) async {
try {
await file.setLastModified(DateTime.now());
} catch (e) {
}
} catch (e) {}
}
String _generateFileName(String url) {
final hash = url.hashCode.abs().toString();
final extension = path.extension(url).isNotEmpty
? path.extension(url)
@@ -203,7 +228,6 @@ class ImageCacheService {
return '$hash$extension';
}
Future<Map<String, dynamic>> getCacheStats() async {
final size = await getCacheSize();
final fileCount = await getCacheFileCount();
@@ -213,13 +237,13 @@ class ImageCacheService {
'cache_size_mb': (size / (1024 * 1024)).toStringAsFixed(2),
'file_count': fileCount,
'cache_directory': _cacheDirectory.path,
'compression_enabled': true,
'compression_algorithm': 'LZ4',
};
}
}
extension CachedImageExtension on String {
Widget getCachedNetworkImage({
Key? key,
double? width,
@@ -259,7 +283,6 @@ extension CachedImageExtension on String {
);
}
Future<void> preloadImage() async {
await ImageCacheService.instance.loadImage(this);
}