generated from dellevin/template
功能优化
This commit is contained in:
@@ -11,6 +11,7 @@ import '../../utils/user_prefs.dart';
|
|||||||
import '../../utils/theme/app_theme.dart';
|
import '../../utils/theme/app_theme.dart';
|
||||||
import '../../utils/toast_util.dart';
|
import '../../utils/toast_util.dart';
|
||||||
import '../../data/database_helper.dart';
|
import '../../data/database_helper.dart';
|
||||||
|
import '../../services/sync/cache_cleaner.dart';
|
||||||
import '../online_search/enhanced_search_settings_page.dart';
|
import '../online_search/enhanced_search_settings_page.dart';
|
||||||
import '../settings/legal_page.dart';
|
import '../settings/legal_page.dart';
|
||||||
import 'app_icon_picker_page.dart';
|
import 'app_icon_picker_page.dart';
|
||||||
@@ -1035,30 +1036,13 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
context: context,
|
context: context,
|
||||||
barrierDismissible: false,
|
barrierDismissible: false,
|
||||||
builder: (_) => const Center(child: CircularProgressIndicator()));
|
builder: (_) => const Center(child: CircularProgressIndicator()));
|
||||||
final appProvider = context.read<AppProvider>();
|
final result = await CacheCleaner.instance.clean(context.read<AppProvider>());
|
||||||
|
|
||||||
// 1. 清理未引用的图片文件
|
|
||||||
final dbImagePaths = await _getAllDbImagePaths(appProvider);
|
|
||||||
final deletedImages = await _cleanImageDirectory(dbImagePaths);
|
|
||||||
|
|
||||||
// 2. 清理孤立的 epub_books 目录
|
|
||||||
final deletedEpubs = await _cleanOrphanedEpubBooks(appProvider);
|
|
||||||
|
|
||||||
// 3. 清理临时目录缓存(分享海报、备份ZIP等)
|
|
||||||
final deletedTemp = await _cleanTempDirectory();
|
|
||||||
|
|
||||||
// 4. 清理空文件夹
|
|
||||||
final deletedEmptyDirs = await _cleanEmptyDirectories();
|
|
||||||
|
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
final total =
|
if (result.total == 0) {
|
||||||
deletedImages + deletedEpubs + deletedTemp + deletedEmptyDirs;
|
|
||||||
if (total == 0) {
|
|
||||||
ToastUtil.show(context, '没有需要清理的缓存');
|
ToastUtil.show(context, '没有需要清理的缓存');
|
||||||
} else {
|
} else {
|
||||||
ToastUtil.show(context,
|
ToastUtil.show(context, result.description);
|
||||||
'已清理 $deletedImages 个孤立图片,$deletedEpubs 个孤立电子书,$deletedTemp 个临时文件,$deletedEmptyDirs 个空文件夹');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -1096,77 +1080,6 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
return paths;
|
return paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<int> _cleanImageDirectory(Set<String> dbImagePaths) async {
|
|
||||||
int deletedCount = 0;
|
|
||||||
try {
|
|
||||||
final appDir = await getApplicationDocumentsDirectory();
|
|
||||||
final imagesDir = Directory('${appDir.path}/images');
|
|
||||||
if (!await imagesDir.exists()) return 0;
|
|
||||||
await for (final entity
|
|
||||||
in imagesDir.list(recursive: true, followLinks: false)) {
|
|
||||||
if (entity is File &&
|
|
||||||
!dbImagePaths.contains(entity.path) &&
|
|
||||||
!path.basename(entity.path).startsWith('avatar')) {
|
|
||||||
try {
|
|
||||||
await entity.delete();
|
|
||||||
deletedCount++;
|
|
||||||
} catch (_) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint('清理图片目录失败: $e');
|
|
||||||
}
|
|
||||||
return deletedCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 清理 epub_books 中孤立的目录(数据库中不存在的)
|
|
||||||
Future<int> _cleanOrphanedEpubBooks(AppProvider provider) async {
|
|
||||||
int deletedCount = 0;
|
|
||||||
try {
|
|
||||||
final db = await DatabaseHelper.instance.database;
|
|
||||||
// 收集数据库中所有引用的 epub_books 子目录名(包括软删除的)
|
|
||||||
final rows = await db.query('reader_books',
|
|
||||||
columns: ['id', 'file_path', 'cover_path', 'is_deleted']);
|
|
||||||
final usedDirs = <String>{};
|
|
||||||
for (final r in rows) {
|
|
||||||
// 只收集未删除的记录对应的目录
|
|
||||||
final isDeleted = r['is_deleted'] == 1 || r['is_deleted'] == true;
|
|
||||||
if (isDeleted) continue;
|
|
||||||
final id = r['id'] as String?;
|
|
||||||
if (id != null && id.isNotEmpty) usedDirs.add(id);
|
|
||||||
_collectEpubDirName(r['file_path'] as String?, usedDirs);
|
|
||||||
_collectEpubDirName(r['cover_path'] as String?, usedDirs);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查 /data/user/0/top.iletter.mooknote/app_flutter/epub_books 路径
|
|
||||||
final appDir = await getApplicationDocumentsDirectory();
|
|
||||||
final possiblePaths = [
|
|
||||||
'${appDir.path}/epub_books',
|
|
||||||
'/data/user/0/top.iletter.mooknote/app_flutter/epub_books',
|
|
||||||
];
|
|
||||||
|
|
||||||
for (final epubPath in possiblePaths) {
|
|
||||||
final epubDir = Directory(epubPath);
|
|
||||||
if (!await epubDir.exists()) continue;
|
|
||||||
|
|
||||||
await for (final entity in epubDir.list(followLinks: false)) {
|
|
||||||
if (entity is Directory) {
|
|
||||||
final dirName = path.basename(entity.path);
|
|
||||||
if (!usedDirs.contains(dirName)) {
|
|
||||||
try {
|
|
||||||
await entity.delete(recursive: true);
|
|
||||||
deletedCount++;
|
|
||||||
} catch (_) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint('清理 epub_books 目录失败: $e');
|
|
||||||
}
|
|
||||||
return deletedCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 从绝对路径中提取 epub_books/ 下的目录名
|
/// 从绝对路径中提取 epub_books/ 下的目录名
|
||||||
void _collectEpubDirName(String? pathStr, Set<String> dirs) {
|
void _collectEpubDirName(String? pathStr, Set<String> dirs) {
|
||||||
if (pathStr == null || pathStr.isEmpty) return;
|
if (pathStr == null || pathStr.isEmpty) return;
|
||||||
@@ -1178,100 +1091,6 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
dirs.add(slashIdx >= 0 ? rest.substring(0, slashIdx) : rest);
|
dirs.add(slashIdx >= 0 ? rest.substring(0, slashIdx) : rest);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<int> _cleanTempDirectory() async {
|
|
||||||
int deletedCount = 0;
|
|
||||||
final now = DateTime.now();
|
|
||||||
|
|
||||||
// 1. 清理临时目录中的分享海报(保留备份ZIP)
|
|
||||||
try {
|
|
||||||
final tempDir = await getTemporaryDirectory();
|
|
||||||
if (await tempDir.exists()) {
|
|
||||||
await for (final entity in tempDir.list(followLinks: false)) {
|
|
||||||
if (entity is File) {
|
|
||||||
final name = path.basename(entity.path);
|
|
||||||
if (name.startsWith('book_poster_') ||
|
|
||||||
name.startsWith('movie_poster_') ||
|
|
||||||
name.startsWith('note_share_') ||
|
|
||||||
name.startsWith('mooknote_download') ||
|
|
||||||
name.startsWith('mooknote_bidir')) {
|
|
||||||
try {
|
|
||||||
final stat = await entity.stat();
|
|
||||||
if (now.difference(stat.modified).inHours >= 1) {
|
|
||||||
await entity.delete();
|
|
||||||
deletedCount++;
|
|
||||||
}
|
|
||||||
} catch (_) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint('清理临时目录失败: $e');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 清理应用缓存目录 (/data/user/0/{package}/cache/)
|
|
||||||
try {
|
|
||||||
final cacheDir = await getApplicationCacheDirectory();
|
|
||||||
if (await cacheDir.exists()) {
|
|
||||||
await for (final entity
|
|
||||||
in cacheDir.list(recursive: true, followLinks: false)) {
|
|
||||||
if (entity is File) {
|
|
||||||
try {
|
|
||||||
await entity.delete();
|
|
||||||
deletedCount++;
|
|
||||||
} catch (_) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint('清理缓存目录失败: $e');
|
|
||||||
}
|
|
||||||
|
|
||||||
return deletedCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 清理 images、epub_books、cache 下的空文件夹
|
|
||||||
Future<int> _cleanEmptyDirectories() async {
|
|
||||||
int deletedCount = 0;
|
|
||||||
try {
|
|
||||||
final appDir = await getApplicationDocumentsDirectory();
|
|
||||||
final cacheDir = await getApplicationCacheDirectory();
|
|
||||||
final dirs = [
|
|
||||||
Directory('${appDir.path}/images'),
|
|
||||||
Directory('${appDir.path}/epub_books'),
|
|
||||||
cacheDir,
|
|
||||||
];
|
|
||||||
for (final dir in dirs) {
|
|
||||||
if (!await dir.exists()) continue;
|
|
||||||
deletedCount += await _removeEmptyDirsRecursive(dir);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint('清理空文件夹失败: $e');
|
|
||||||
}
|
|
||||||
return deletedCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 递归删除空子文件夹(自底向上),不删除根目录本身
|
|
||||||
Future<int> _removeEmptyDirsRecursive(Directory dir) async {
|
|
||||||
int count = 0;
|
|
||||||
try {
|
|
||||||
final children = await dir.list(followLinks: false).toList();
|
|
||||||
for (final child in children) {
|
|
||||||
if (child is Directory) {
|
|
||||||
count += await _removeEmptyDirsRecursive(child);
|
|
||||||
final remaining = await child.list(followLinks: false).toList();
|
|
||||||
if (remaining.isEmpty) {
|
|
||||||
try {
|
|
||||||
await child.delete();
|
|
||||||
count++;
|
|
||||||
} catch (_) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (_) {}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── 扫描方法(只统计不删除) ──────────────────────────────────────────────
|
// ─── 扫描方法(只统计不删除) ──────────────────────────────────────────────
|
||||||
|
|
||||||
/// 返回 (文件数, 总字节数)
|
/// 返回 (文件数, 总字节数)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import '../../providers/app_provider.dart';
|
import '../../providers/app_provider.dart';
|
||||||
import '../../services/sync/backup_service.dart';
|
import '../../services/sync/backup_service.dart';
|
||||||
|
import '../../services/sync/cache_cleaner.dart';
|
||||||
import '../../utils/toast_util.dart';
|
import '../../utils/toast_util.dart';
|
||||||
|
|
||||||
/// 本地备份页面
|
/// 本地备份页面
|
||||||
@@ -334,6 +335,9 @@ class _BackupPageState extends State<BackupPage> {
|
|||||||
setState(() => _isExporting = true);
|
setState(() => _isExporting = true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// 先清理缓存
|
||||||
|
await CacheCleaner.instance.clean(context.read<AppProvider>());
|
||||||
|
|
||||||
final result = await BackupService.instance.exportDataWithImages();
|
final result = await BackupService.instance.exportDataWithImages();
|
||||||
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
@@ -431,6 +435,9 @@ class _BackupPageState extends State<BackupPage> {
|
|||||||
setState(() => _isImporting = true);
|
setState(() => _isImporting = true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// 先清理缓存
|
||||||
|
await CacheCleaner.instance.clean(context.read<AppProvider>());
|
||||||
|
|
||||||
final result = await BackupService.instance.importData();
|
final result = await BackupService.instance.importData();
|
||||||
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import '../../utils/toast_util.dart';
|
import '../../utils/toast_util.dart';
|
||||||
import '../../services/sync/webdav_service.dart';
|
import '../../services/sync/webdav_service.dart';
|
||||||
|
import '../../services/sync/cache_cleaner.dart';
|
||||||
import '../../providers/app_provider.dart';
|
import '../../providers/app_provider.dart';
|
||||||
|
|
||||||
/// WebDAV 备份页面
|
/// WebDAV 备份页面
|
||||||
@@ -126,6 +127,11 @@ class _WebDAVSyncPageState extends State<WebDAVSyncPage> {
|
|||||||
setState(() => _isLoading = true);
|
setState(() => _isLoading = true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// 先清理缓存
|
||||||
|
setState(() => _syncStep = '正在清理缓存...');
|
||||||
|
await Future.delayed(Duration.zero);
|
||||||
|
await CacheCleaner.instance.clean(context.read<AppProvider>());
|
||||||
|
|
||||||
SyncResult result;
|
SyncResult result;
|
||||||
|
|
||||||
if (_syncDirection == SyncDirection.upload) {
|
if (_syncDirection == SyncDirection.upload) {
|
||||||
|
|||||||
238
lib/services/sync/cache_cleaner.dart
Normal file
238
lib/services/sync/cache_cleaner.dart
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
import '../../providers/app_provider.dart';
|
||||||
|
import '../../data/database_helper.dart';
|
||||||
|
|
||||||
|
/// 缓存清理服务
|
||||||
|
class CacheCleaner {
|
||||||
|
CacheCleaner._();
|
||||||
|
static final CacheCleaner instance = CacheCleaner._();
|
||||||
|
|
||||||
|
/// 执行完整缓存清理,返回各分类删除数量
|
||||||
|
Future<CacheCleanResult> clean(AppProvider provider) async {
|
||||||
|
final dbImagePaths = await _getAllDbImagePaths(provider);
|
||||||
|
final deletedImages = await _cleanImageDirectory(dbImagePaths);
|
||||||
|
final deletedEpubs = await _cleanOrphanedEpubBooks(provider);
|
||||||
|
final deletedTemp = await _cleanTempDirectory();
|
||||||
|
final deletedEmptyDirs = await _cleanEmptyDirectories();
|
||||||
|
return CacheCleanResult(
|
||||||
|
images: deletedImages,
|
||||||
|
epubs: deletedEpubs,
|
||||||
|
temp: deletedTemp,
|
||||||
|
emptyDirs: deletedEmptyDirs,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Set<String>> _getAllDbImagePaths(AppProvider provider) async {
|
||||||
|
final paths = <String>{};
|
||||||
|
for (final movie in provider.movies) {
|
||||||
|
if (movie.posterPath?.isNotEmpty == true) paths.add(movie.posterPath!);
|
||||||
|
}
|
||||||
|
for (final book in provider.books) {
|
||||||
|
if (book.coverPath?.isNotEmpty == true) paths.add(book.coverPath!);
|
||||||
|
}
|
||||||
|
for (final note in provider.notes) {
|
||||||
|
for (final p in note.images) {
|
||||||
|
if (p.isNotEmpty) paths.add(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (final movieId in provider.movies.map((m) => m.id)) {
|
||||||
|
for (final poster in await provider.getMoviePosters(movieId)) {
|
||||||
|
if (poster.posterPath.isNotEmpty) paths.add(poster.posterPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (final game in provider.games) {
|
||||||
|
if (game.coverPath?.isNotEmpty == true) paths.add(game.coverPath!);
|
||||||
|
}
|
||||||
|
for (final gameId in provider.games.map((g) => g.id)) {
|
||||||
|
for (final screenshot in await provider.getGameScreenshots(gameId)) {
|
||||||
|
if (screenshot.screenshotPath.isNotEmpty) paths.add(screenshot.screenshotPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> _cleanImageDirectory(Set<String> dbImagePaths) async {
|
||||||
|
int deletedCount = 0;
|
||||||
|
try {
|
||||||
|
final appDir = await getApplicationDocumentsDirectory();
|
||||||
|
final imagesDir = Directory('${appDir.path}/images');
|
||||||
|
if (!await imagesDir.exists()) return 0;
|
||||||
|
await for (final entity in imagesDir.list(recursive: true, followLinks: false)) {
|
||||||
|
if (entity is File &&
|
||||||
|
!dbImagePaths.contains(entity.path) &&
|
||||||
|
!path.basename(entity.path).startsWith('avatar')) {
|
||||||
|
try {
|
||||||
|
await entity.delete();
|
||||||
|
deletedCount++;
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('清理图片目录失败: $e');
|
||||||
|
}
|
||||||
|
return deletedCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> _cleanOrphanedEpubBooks(AppProvider provider) async {
|
||||||
|
int deletedCount = 0;
|
||||||
|
try {
|
||||||
|
final db = await DatabaseHelper.instance.database;
|
||||||
|
final rows = await db.query('reader_books', columns: ['id', 'file_path', 'cover_path', 'is_deleted']);
|
||||||
|
final usedDirs = <String>{};
|
||||||
|
for (final r in rows) {
|
||||||
|
final isDeleted = r['is_deleted'] == 1 || r['is_deleted'] == true;
|
||||||
|
if (isDeleted) continue;
|
||||||
|
final id = r['id'] as String?;
|
||||||
|
if (id != null && id.isNotEmpty) usedDirs.add(id);
|
||||||
|
_collectEpubDirName(r['file_path'] as String?, usedDirs);
|
||||||
|
_collectEpubDirName(r['cover_path'] as String?, usedDirs);
|
||||||
|
}
|
||||||
|
|
||||||
|
final appDir = await getApplicationDocumentsDirectory();
|
||||||
|
final possiblePaths = [
|
||||||
|
'${appDir.path}/epub_books',
|
||||||
|
'/data/user/0/top.iletter.mooknote/app_flutter/epub_books',
|
||||||
|
];
|
||||||
|
|
||||||
|
for (final epubPath in possiblePaths) {
|
||||||
|
final epubDir = Directory(epubPath);
|
||||||
|
if (!await epubDir.exists()) continue;
|
||||||
|
await for (final entity in epubDir.list(followLinks: false)) {
|
||||||
|
if (entity is Directory) {
|
||||||
|
final dirName = path.basename(entity.path);
|
||||||
|
if (!usedDirs.contains(dirName)) {
|
||||||
|
try {
|
||||||
|
await entity.delete(recursive: true);
|
||||||
|
deletedCount++;
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('清理 epub_books 目录失败: $e');
|
||||||
|
}
|
||||||
|
return deletedCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _collectEpubDirName(String? pathStr, Set<String> dirs) {
|
||||||
|
if (pathStr == null || pathStr.isEmpty) return;
|
||||||
|
final marker = '/epub_books/';
|
||||||
|
final idx = pathStr.indexOf(marker);
|
||||||
|
if (idx < 0) return;
|
||||||
|
final rest = pathStr.substring(idx + marker.length);
|
||||||
|
final slashIdx = rest.indexOf('/');
|
||||||
|
dirs.add(slashIdx >= 0 ? rest.substring(0, slashIdx) : rest);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> _cleanTempDirectory() async {
|
||||||
|
int deletedCount = 0;
|
||||||
|
final now = DateTime.now();
|
||||||
|
|
||||||
|
try {
|
||||||
|
final tempDir = await getTemporaryDirectory();
|
||||||
|
if (await tempDir.exists()) {
|
||||||
|
await for (final entity in tempDir.list(followLinks: false)) {
|
||||||
|
if (entity is File) {
|
||||||
|
final name = path.basename(entity.path);
|
||||||
|
if (name.startsWith('book_poster_') ||
|
||||||
|
name.startsWith('movie_poster_') ||
|
||||||
|
name.startsWith('note_share_') ||
|
||||||
|
name.startsWith('mooknote_download') ||
|
||||||
|
name.startsWith('mooknote_bidir')) {
|
||||||
|
try {
|
||||||
|
final stat = await entity.stat();
|
||||||
|
if (now.difference(stat.modified).inHours >= 1) {
|
||||||
|
await entity.delete();
|
||||||
|
deletedCount++;
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('清理临时目录失败: $e');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final cacheDir = await getApplicationCacheDirectory();
|
||||||
|
if (await cacheDir.exists()) {
|
||||||
|
await for (final entity in cacheDir.list(recursive: true, followLinks: false)) {
|
||||||
|
if (entity is File) {
|
||||||
|
try {
|
||||||
|
await entity.delete();
|
||||||
|
deletedCount++;
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('清理缓存目录失败: $e');
|
||||||
|
}
|
||||||
|
|
||||||
|
return deletedCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> _cleanEmptyDirectories() async {
|
||||||
|
int deletedCount = 0;
|
||||||
|
try {
|
||||||
|
final appDir = await getApplicationDocumentsDirectory();
|
||||||
|
final cacheDir = await getApplicationCacheDirectory();
|
||||||
|
final dirs = [
|
||||||
|
Directory('${appDir.path}/images'),
|
||||||
|
Directory('${appDir.path}/epub_books'),
|
||||||
|
cacheDir,
|
||||||
|
];
|
||||||
|
for (final dir in dirs) {
|
||||||
|
if (!await dir.exists()) continue;
|
||||||
|
deletedCount += await _removeEmptyDirsRecursive(dir);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('清理空文件夹失败: $e');
|
||||||
|
}
|
||||||
|
return deletedCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> _removeEmptyDirsRecursive(Directory dir) async {
|
||||||
|
int count = 0;
|
||||||
|
try {
|
||||||
|
final children = await dir.list(followLinks: false).toList();
|
||||||
|
for (final child in children) {
|
||||||
|
if (child is Directory) {
|
||||||
|
count += await _removeEmptyDirsRecursive(child);
|
||||||
|
final remaining = await child.list(followLinks: false).toList();
|
||||||
|
if (remaining.isEmpty) {
|
||||||
|
try {
|
||||||
|
await child.delete();
|
||||||
|
count++;
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CacheCleanResult {
|
||||||
|
final int images;
|
||||||
|
final int epubs;
|
||||||
|
final int temp;
|
||||||
|
final int emptyDirs;
|
||||||
|
|
||||||
|
const CacheCleanResult({
|
||||||
|
required this.images,
|
||||||
|
required this.epubs,
|
||||||
|
required this.temp,
|
||||||
|
required this.emptyDirs,
|
||||||
|
});
|
||||||
|
|
||||||
|
int get total => images + epubs + temp + emptyDirs;
|
||||||
|
|
||||||
|
String get description =>
|
||||||
|
'已清理 $images 个孤立图片,$epubs 个孤立电子书,$temp 个临时文件,$emptyDirs 个空文件夹';
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
name: mooknote
|
name: mooknote
|
||||||
description: "app for tracking movies, books, and notes"
|
description: "app for tracking movies, books, and notes"
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
version: 0.2.5
|
version: 0.2.6
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.5.0
|
sdk: ^3.5.0
|
||||||
|
|||||||
Reference in New Issue
Block a user