refa: cache manager (#2266)

* opt: image utils

* refa: cache manager
This commit is contained in:
My-Responsitories
2026-06-02 14:09:36 +00:00
committed by GitHub
parent e619946b9c
commit 66a191b018
26 changed files with 126 additions and 196 deletions

View File

@@ -1,27 +1,29 @@
import 'dart:io' show Directory, File;
import 'package:PiliPlus/utils/extension/file_ext.dart';
import 'package:PiliPlus/utils/platform_utils.dart';
import 'package:PiliPlus/utils/storage_pref.dart';
import 'package:cached_network_image_ce/cached_network_image.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
abstract final class CacheManager {
static late final DefaultCacheManager manager;
static Future<void> ensureInitialized() =>
DefaultCacheManager.init().then((i) => manager = i);
// 获取缓存目录
@pragma('vm:notify-debugger-on-exception')
static Future<int> loadApplicationCache([
final num maxSize = double.infinity,
]) async {
static Future<int> loadApplicationCache() async {
try {
final Directory tempDirectory = await getTemporaryDirectory();
if (PlatformUtils.isDesktop) {
final dir = Directory('${tempDirectory.path}/libCachedImageData');
if (dir.existsSync()) {
return await getTotalSizeOfFilesInDir(dir, maxSize);
}
return 0;
return manager.getTotalLength();
}
if (tempDirectory.existsSync()) {
return await getTotalSizeOfFilesInDir(tempDirectory, maxSize);
return await getTotalSizeOfFilesInDir(tempDirectory);
}
} catch (_) {}
return 0;
@@ -29,16 +31,21 @@ abstract final class CacheManager {
// 循环计算文件的大小
@pragma('vm:notify-debugger-on-exception')
static Future<int> getTotalSizeOfFilesInDir(
final Directory file, [
final num maxSize = double.infinity,
]) async {
final children = file.list(recursive: true);
static Future<int> getTotalSizeOfFilesInDir(final Directory file) async {
int total = 0;
await for (final child in children) {
await for (final child in file.list(recursive: false)) {
if (child is File) {
total += await child.length();
if (total >= maxSize) break;
} else if (child is Directory) {
if (path.equals(child.path, manager.cacheDir)) {
total += manager.getTotalLength();
} else {
await for (final i in child.list(recursive: true)) {
if (i is File) {
total += await i.length();
}
}
}
}
}
return total;
@@ -57,16 +64,13 @@ abstract final class CacheManager {
}
// 清除 Library/Caches 目录及文件缓存
@pragma('vm:notify-debugger-on-exception')
static Future<void> clearLibraryCache() async {
try {
final Directory tempDirectory = await getTemporaryDirectory();
if (PlatformUtils.isDesktop) {
final dir = Directory('${tempDirectory.path}/libCachedImageData');
if (dir.existsSync()) {
await dir.delete(recursive: true);
}
return;
}
await manager.emptyCache();
if (PlatformUtils.isDesktop) return;
final tempDirectory = await getTemporaryDirectory();
if (tempDirectory.existsSync()) {
final children = tempDirectory.list(recursive: false);
await for (final file in children) {
@@ -77,12 +81,16 @@ abstract final class CacheManager {
}
static Future<void> autoClearCache() async {
// TODO: remove
Directory(
'${(await getTemporaryDirectory()).path}/libCachedImageData',
).tryDel(recursive: true);
if (Pref.autoClearCache) {
await clearLibraryCache();
} else {
final maxCacheSize = Pref.maxCacheSize;
if (maxCacheSize != 0) {
final currCache = await loadApplicationCache(maxCacheSize);
final currCache = await loadApplicationCache();
if (currCache >= maxCacheSize) {
await clearLibraryCache();
}

View File

@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:io' show File, Platform;
import 'dart:math' as math;
import 'dart:typed_data' show Uint8List;
@@ -14,10 +15,10 @@ import 'package:PiliPlus/utils/platform_utils.dart';
import 'package:PiliPlus/utils/share_utils.dart';
import 'package:PiliPlus/utils/storage_pref.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:cached_network_image_ce/cached_network_image.dart';
import 'package:dio/dio.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:intl/intl.dart' show DateFormat;
import 'package:live_photo_maker/live_photo_maker.dart';
@@ -36,19 +37,16 @@ abstract final class ImageUtils {
static Future<void> onShareImg(String url) async {
try {
SmartDialog.showLoading();
final path = '$tmpDirPath/${Utils.getFileName(url)}';
final res = await Request().downloadFile(url.http2https, path);
final res = await DefaultCacheManager.instance!.getSingleFile(
url.http2https,
);
SmartDialog.dismiss();
if (res.statusCode == 200) {
await SharePlus.instance
.share(
ShareParams(
files: [XFile(path)],
sharePositionOrigin: await ShareUtils.sharePositionOrigin,
),
)
.whenComplete(File(path).tryDel);
}
await SharePlus.instance.share(
ShareParams(
files: [XFile(res.path)],
sharePositionOrigin: await ShareUtils.sharePositionOrigin,
),
);
} catch (e) {
SmartDialog.showToast(e.toString());
}
@@ -82,12 +80,12 @@ abstract final class ImageUtils {
}
}
static Future<bool> checkPermissionDependOnSdkInt() {
static FutureOr<bool> checkPermissionDependOnSdkInt() {
if (Platform.isAndroid) {
if (DeviceUtils.sdkInt < 29) {
return requestPer();
} else {
return Future.syncValue(true);
return true;
}
}
return requestPer();
@@ -105,8 +103,6 @@ abstract final class ImageUtils {
}
if (!silentDownImg) SmartDialog.showLoading(msg: '正在下载');
late String imageName = "cover_${Utils.getFileName(url)}";
late String imagePath = '$tmpDirPath/$imageName';
String videoName = "video_${Utils.getFileName(liveUrl)}";
String videoPath = '$tmpDirPath/$videoName';
@@ -114,22 +110,17 @@ abstract final class ImageUtils {
if (res.statusCode != 200) throw '${res.statusCode}';
if (Platform.isIOS) {
final res1 = await Request().downloadFile(url.http2https, imagePath);
if (res1.statusCode != 200) throw '${res1.statusCode}';
final imageFile = await DefaultCacheManager.instance!.getSingleFile(
url.http2https,
);
if (!silentDownImg) SmartDialog.showLoading(msg: '正在保存');
bool success =
await LivePhotoMaker.create(
coverImage: imagePath,
imagePath: null,
voicePath: videoPath,
width: width,
height: height,
).whenComplete(
() {
File(videoPath).tryDel();
File(imagePath).tryDel();
},
);
bool success = await LivePhotoMaker.create(
coverImage: imageFile.path,
imagePath: null,
voicePath: videoPath,
width: width,
height: height,
).whenComplete(File(videoPath).tryDel);
if (success) {
SmartDialog.showToast(' 已保存 ');
} else {
@@ -154,10 +145,7 @@ abstract final class ImageUtils {
}
}
static Future<bool> downloadImg(
List<String> imgList, [
CacheManager? manager,
]) async {
static Future<bool> downloadImg(List<String> imgList) async {
if (PlatformUtils.isMobile && !await checkPermissionDependOnSdkInt()) {
return false;
}
@@ -174,31 +162,10 @@ abstract final class ImageUtils {
final futures = imgList.map((url) async {
final name = Utils.getFileName(url);
final file = (await (manager ?? DefaultCacheManager()).getFileFromCache(
final file = await DefaultCacheManager.instance!.getSingleFile(
url.http2https,
))?.file;
if (file == null) {
final String filePath = '$tmpDirPath/$name';
final response = await Request().downloadFile(
url.http2https,
filePath,
cancelToken: cancelToken,
);
return (
filePath: filePath,
name: name,
statusCode: response.statusCode,
del: true,
);
} else {
return (
filePath: file.path,
name: name,
statusCode: 200,
del: false,
);
}
);
return (filePath: file.path, name: name, statusCode: 200);
});
final result = await Future.wait(futures, eagerError: true);
bool success = true;
@@ -206,7 +173,6 @@ abstract final class ImageUtils {
final delList = <String>[];
final saveList = <SaveFileData>[];
for (final i in result) {
if (i.del) delList.add(i.filePath);
if (i.statusCode == 200) {
saveList.add(
SaveFileData(
@@ -226,11 +192,7 @@ abstract final class ImageUtils {
} else {
for (final res in result) {
if (res.statusCode == 200) {
await saveFileImg(
filePath: res.filePath,
fileName: res.name,
del: res.del,
);
await saveFileImg(filePath: res.filePath, fileName: res.name);
} else {
success = false;
}
@@ -336,7 +298,6 @@ abstract final class ImageUtils {
required String fileName,
FileType type = FileType.image,
bool needToast = false,
bool del = true,
}) async {
final file = File(filePath);
if (!file.existsSync()) {
@@ -351,7 +312,6 @@ abstract final class ImageUtils {
albumPath: _albumPath,
skipIfExists: false,
);
if (del) file.tryDel();
} else {
final savePath = await FilePicker.saveFile(
type: type,
@@ -363,7 +323,6 @@ abstract final class ImageUtils {
return;
}
await file.copy(savePath);
if (del) file.tryDel();
res = SaveResult(true, null);
}
if (needToast) {