Quellcode durchsuchen

[feat]优化文件计算大小,新增文件大小分组

云天逵 vor 1 Jahr
Ursprung
Commit
9d7d27d79f

+ 45 - 47
lib/base/base_photo_controller.dart

@@ -13,78 +13,76 @@ import 'package:clean/utils/toast_util.dart';
 import 'package:clean/data/bean/photos_type.dart';
 import 'package:clean/module/photo_preview/photo_preview_view.dart';
 
+import '../utils/file_size_calculator_util.dart';
+
 abstract class BasePhotoController extends BaseController {
   final RxList<PhotoGroup> photoGroups = <PhotoGroup>[].obs;
   final RxDouble selectedFilesSize = 0.0.obs;
   RxInt selectedFileCount = 0.obs;
   final RxSet<String> selectedPhotosIds = <String>{}.obs;
 
-
   // 将selectedFilesSize转成String类型,然后单位转换,如果超过1MB,则转成MB,超过1GB,则转成GB,否则KB
   String get selectedFilesSizeString {
-    print("BasePhotoController  selectedFilesSize.value ${selectedFilesSize.value}");
+    debugPrint(
+        "BasePhotoController  selectedFilesSize.value ${selectedFilesSize.value}");
     final double sizeInKB = selectedFilesSize.value;
 
-    if (sizeInKB >= 1024 * 1024) {  // 先检查最大单位(GB)
+    if (sizeInKB >= 1024 * 1024) {
+      // 先检查最大单位(GB)
       return "${(sizeInKB / (1024 * 1024)).toStringAsFixed(2)}GB";
-    } else if (sizeInKB >= 1024) {  // 然后检查MB
+    } else if (sizeInKB >= 1024) {
+      // 然后检查MB
       return "${(sizeInKB / 1024).toStringAsFixed(2)}MB";
-    } else {  // 最后是KB
+    } else {
+      // 最后是KB
       return "${sizeInKB.toStringAsFixed(2)}KB";
     }
   }
 
   Future<void> updateSelectedFilesSize() async {
-
-
     if (selectedPhotosIds.isEmpty) {
-
       selectedFilesSize.value = 0;
-
-      print("BasePhotoController updateSelectedFilesSize isEmpty");
-
       return;
     }
 
-    print("BasePhotoController updateSelectedFilesSize");
-
     double totalSize = 0;
+    final uncasedIds = selectedPhotosIds.where((id) => !FileSizeCalculatorUtil.fileSizeCache.containsKey(id)).toSet();
 
-    // 创建一个 Future 列表,用于并行获取文件大小
-    List<Future<double>> sizeFutures =
-        selectedPhotosIds.map((id) => getFileSize(id)).toList();
+    // **1️⃣ 先处理缓存中的文件**
 
-    // 等待所有文件大小计算完成
-    List<double> sizes = await Future.wait(sizeFutures);
-    // 计算总大小
-    totalSize = sizes.fold(0, (sum, size) => sum + size);
-    if (selectedPhotosIds.isEmpty) {
-      selectedFilesSize.value = 0;
-      return;
-    }
-    selectedFilesSize.value = totalSize; // 更新总大小
-    PhotoManager.clearFileCache();
-  }
+    totalSize = selectedPhotosIds.fold(0, (prev, id) => prev + (FileSizeCalculatorUtil.fileSizeCache[id] ?? 0));
 
-  Future<double> getFileSize(String assetId) async {
-    // 先检查缓存
-    if (ImagePickerUtil.fileSizeCache.containsKey(assetId)) {
-      return ImagePickerUtil.fileSizeCache[assetId]!;
-    }
 
-    final entity = await AssetEntity.fromId(assetId);
-    if (entity != null) {
-      final file = await entity.file;
-      if (file != null) {
-        double size = await file.length() / 1024; // 转换为 KB
-        ImagePickerUtil.fileSizeCache[assetId] = size; // 缓存文件大小
-        file.delete();
-        return size;
+    // **2️⃣ 分批处理未缓存的文件**
+    const batchSize = 50;
+    for (int i = 0; i < uncasedIds.length; i += batchSize) {
+      if (selectedPhotosIds.isEmpty) {
+        selectedFilesSize.value = 0;
+        return;
       }
+
+      final batch = uncasedIds.skip(i).take(batchSize);
+      final sizes = await Future.wait(batch.map(FileSizeCalculatorUtil.getFileSize));
+
+      totalSize += sizes.fold(0, (sum, size) => sum + size);
+
+      // **再检查一次是否被清空,避免无意义计算**
+      if (selectedPhotosIds.isEmpty) {
+        selectedFilesSize.value = 0;
+        return;
+      }
+
+      // **减少 UI 更新频率**
+      if (i % (batchSize * 2) == 0 || i + batchSize >= uncasedIds.length) {
+        selectedFilesSize.value = totalSize;
+      }
+
+      await Future.delayed(Duration.zero);
     }
-    return 0;
-  }
 
+    selectedFilesSize.value = totalSize; // 确保最终更新总大小
+    PhotoManager.clearFileCache();
+  }
 
   // 切换图片选中状态
   Future<void> toggleImageSelection(
@@ -104,8 +102,8 @@ abstract class BasePhotoController extends BaseController {
     // 更新文件大小
     if (selected) {
       // 如果选中,增加文件大小
-      if (ImagePickerUtil.fileSizeCache.containsKey(image.id)) {
-        selectedFilesSize.value += ImagePickerUtil.fileSizeCache[image.id]!;
+      if (FileSizeCalculatorUtil.fileSizeCache.containsKey(image.id)) {
+        selectedFilesSize.value += FileSizeCalculatorUtil.fileSizeCache[image.id]!;
       } else {
         final file = await image.file;
         if (file != null) {
@@ -114,8 +112,8 @@ abstract class BasePhotoController extends BaseController {
       }
     } else {
       // 如果取消选中,减少文件大小
-      if (ImagePickerUtil.fileSizeCache.containsKey(image.id)) {
-        selectedFilesSize.value -= ImagePickerUtil.fileSizeCache[image.id]!;
+      if (FileSizeCalculatorUtil.fileSizeCache.containsKey(image.id)) {
+        selectedFilesSize.value -= FileSizeCalculatorUtil.fileSizeCache[image.id]!;
       } else {
         final file = await image.file;
         if (file != null) {
@@ -177,7 +175,7 @@ abstract class BasePhotoController extends BaseController {
       selectedFilesSize.value = 0;
       return;
     }
-    await updateSelectedFilesSize();
+    updateSelectedFilesSize();
   }
 
   @override

+ 30 - 11
lib/module/calendar/calendar_controller.dart

@@ -48,17 +48,23 @@ class CalendarController extends BaseController {
           .sort((a, b) => b.createDateTime.compareTo(a.createDateTime)); // 时间排序
       monthlyAlbums
           .add(PhotoGroup(month: month, images: assets, isSelected: false));
-    });
 
+    });
+    // 计算
+    for (var album in monthlyAlbums) {
+      album.initTotalSize();
+    }
     // 打印结果
     for (var album in monthlyAlbums) {
-      print('${album.month}: ${album.images.length} 张照片');
+      debugPrint(
+          'CalendarController month: ${album.month}, images: ${album.images.length} ,totalSize: ${album.totalSize}');
     }
   }
 
   // 点击月份卡片,跳转到对应月份的照片详情页
   void clickMonthCard(PhotoGroup photoGroup) {
-    print("clickMonthCard");
+    print("clickMonthCard photoGroup: ${photoGroup.totalSize}");
+
     CalendarMonthPage.start(photoGroup: photoGroup);
   }
 
@@ -81,21 +87,33 @@ class CalendarController extends BaseController {
       case SortType.oldest:
         imageList.sort((a, b) => a.createDateTime.compareTo(b.createDateTime));
         break;
-      // case SortType.largeSize:
-      //   break;
+      case SortType.largeSize:
+        // 先把目前monthlyAlbums中的每个月的大小记录到map中,然后根据这里面的大小进行排序
+        Map<String, double> monthSizeMap = {};
+        for (var album in monthlyAlbums) {
+          print(
+              "CalendarController sortAssets album: ${album.month}, totalSize: ${album.totalSize}");
+          monthSizeMap[album.month!] = album.totalSize.value;
+        }
+        monthlyAlbums.sort((a, b) =>
+            monthSizeMap[b.month!]!.compareTo(monthSizeMap[a.month!]!));
+        imageList.clear();
+        for (var album in monthlyAlbums) {
+          imageList.addAll(album.images);
+        }
+        break;
       case SortType.mostQuantity:
         monthlyAlbums
             .sort((a, b) => b.images.length.compareTo(a.images.length));
         imageList.clear();
         for (var album in monthlyAlbums) {
-          imageList.addAll(album.images); // 将每个月的照片重新按顺序添加到 imageList
+          imageList.addAll(album.images);
         }
         break;
     }
     updateMonthlyAssets(); // 排序后更新分组
   }
 
-
   // 显示排序弹窗
 // 显示排序弹窗
   void clickSort() {
@@ -144,8 +162,8 @@ class CalendarController extends BaseController {
               sortOption(SortType.oldest, 'Oldest'),
               SizedBox(height: 14.h),
               // Large Size
-              // sortOption(SortType.largeSize, 'Large Size'),
-              // SizedBox(height: 14.h),
+              sortOption(SortType.largeSize, 'Large Size'),
+              SizedBox(height: 14.h),
               // Most Quantity
               sortOption(SortType.mostQuantity, 'Most Quantity'),
               SizedBox(height: 55.h),
@@ -176,7 +194,8 @@ class CalendarController extends BaseController {
         child: Text(
           title,
           style: TextStyle(
-            color: isSelected ? Colors.white : Colors.white.withValues(alpha: 0.8),
+            color:
+                isSelected ? Colors.white : Colors.white.withValues(alpha: 0.8),
             fontSize: isSelected ? 16.sp : 14.sp,
             fontWeight: isSelected ? FontWeight.w700 : FontWeight.w500,
           ),
@@ -190,6 +209,6 @@ class CalendarController extends BaseController {
 enum SortType {
   latest, // 最新
   oldest, // 最旧
-  // largeSize, // 按文件组大小排序
+  largeSize, // 按文件组大小排序
   mostQuantity // 按每个月照片数量排序
 }

+ 77 - 70
lib/module/calendar/calendar_month_controller.dart

@@ -9,6 +9,7 @@ import 'package:wechat_assets_picker/wechat_assets_picker.dart';
 import '../../data/repositories/user_repository.dart';
 import '../../dialog/photo_delete_finish_dialog.dart';
 import '../../dialog/photo_deleting_dialog.dart';
+import '../../utils/file_size_calculator_util.dart';
 import '../../utils/toast_util.dart';
 import '../image_picker/image_picker_util.dart';
 import '../people_photo/photo_group.dart';
@@ -18,8 +19,6 @@ class CalendarMonthController extends BaseController {
   late final Rx<PhotoGroup> photoGroup =
       PhotoGroup(isSelected: false, images: []).obs;
 
-
-
   // 是否是选择模式
   RxBool isSelectMode = false.obs;
 
@@ -44,7 +43,6 @@ class CalendarMonthController extends BaseController {
     final newValue = !photoGroup.value.isSelected.value;
     photoGroup.value.toggleSelectAll(newValue);
 
-
     // 更新选中状态
     for (var image in imagesList) {
       updateSelectedPhotosIds(image.id, newValue);
@@ -55,59 +53,54 @@ class CalendarMonthController extends BaseController {
   clickDelete() async {
     debugPrint('CalendarMonthController clickDelete');
     if (userRepository.isVip()) {
-
-    if (photoGroup.value.selectedPhotosIds.isNotEmpty) {
-      photoDeletingDialog();
-      // 获取要删除的资产
-      final assetsToDelete = photoGroup.value.images
-          .where(
-            (asset) => photoGroup.value.selectedPhotosIds.contains(asset.id),
-      )
-          .toList();
-
-      // 调用方法会返回被删除的资源,如果全部失败会返回空列表。
-      final List<String> result = await PhotoManager.editor.deleteWithIds(
-        assetsToDelete.map((e) => e.id).toList(),
-      );
-
-
-      //   比较result和selectedPhotosIds,如果result和selectedPhotosIds相等,说明删除成功,走下面的逻辑
-      // 如果不相等,说明有删除失败的,走else逻辑
-      if (result.length == photoGroup.value.selectedPhotosIds.length) {
-
-        debugPrint("CalendarMonthController delete ${photoGroup.value.selectedPhotosIds}");
-        ImagePickerUtil.updatePhotoData(
-            photoGroup.value.selectedPhotosIds);
-        cleanSelections();
-        ToastUtil.show('Delete success');
-        Future.delayed(Duration(seconds: 2), () {
+      if (photoGroup.value.selectedPhotosIds.isNotEmpty) {
+        photoDeletingDialog();
+        // 获取要删除的资产
+        final assetsToDelete = photoGroup.value.images
+            .where(
+              (asset) => photoGroup.value.selectedPhotosIds.contains(asset.id),
+            )
+            .toList();
+
+        // 调用方法会返回被删除的资源,如果全部失败会返回空列表。
+        final List<String> result = await PhotoManager.editor.deleteWithIds(
+          assetsToDelete.map((e) => e.id).toList(),
+        );
+
+        //   比较result和selectedPhotosIds,如果result和selectedPhotosIds相等,说明删除成功,走下面的逻辑
+        // 如果不相等,说明有删除失败的,走else逻辑
+        if (result.length == photoGroup.value.selectedPhotosIds.length) {
+          debugPrint(
+              "CalendarMonthController delete ${photoGroup.value.selectedPhotosIds}");
+          ImagePickerUtil.updatePhotoData(photoGroup.value.selectedPhotosIds);
+          cleanSelections();
+          ToastUtil.show('Delete success');
+          Future.delayed(Duration(seconds: 2), () {
+            SmartDialog.dismiss(tag: 'photoDeletingDialog');
+            photoDeleteFinishDialog();
+          });
+        } else {
           SmartDialog.dismiss(tag: 'photoDeletingDialog');
-          photoDeleteFinishDialog();
-        });
-      } else {
-        SmartDialog.dismiss(tag: 'photoDeletingDialog');
-        // 删除失败
-        ToastUtil.show("Delete failed");
+          // 删除失败
+          ToastUtil.show("Delete failed");
+        }
       }
-    }
     } else {
       StorePage.start();
     }
   }
+
   //删除成功清除选中的图片
   void cleanSelections() async {
-    photoGroup.value.images
-        .removeWhere((element) => photoGroup.value.selectedPhotosIds.contains(element.id));
-
+    photoGroup.value.images.removeWhere(
+        (element) => photoGroup.value.selectedPhotosIds.contains(element.id));
 
     photoGroup.value.selectedPhotosIds.clear();
 
-
     if (photoGroup.value.images.isEmpty) {
       return;
     }
     updateSelectedFilesSize();
-
   }
 
   Future<void> updateSelectedFilesSize() async {
@@ -118,36 +111,48 @@ class CalendarMonthController extends BaseController {
     }
 
     double totalSize = 0;
-    final selectedImages = photoGroup.value.images;
-
-    // 获取选中图片的大小(异步并行执行)
-    final futures = photoGroup.value.selectedPhotosIds.map((assetId) async {
-      if (ImagePickerUtil.fileSizeCache.containsKey(assetId)) {
-        totalSize += ImagePickerUtil.fileSizeCache[assetId]!;
-      } else {
-        final image = selectedImages.firstWhere((image) => image.id == assetId);
-        final file = await image.file;
-        if (file != null) {
-          final size = (await file.length()) / 1024; // 转换为KB
-          totalSize += size;
-          ImagePickerUtil.fileSizeCache[assetId] = size;
-          file.delete();
-        }
+    final uncasedIds = photoGroup.value.selectedPhotosIds
+        .where((id) => !FileSizeCalculatorUtil.fileSizeCache.containsKey(id))
+        .toSet();
+
+    // **1️⃣ 先处理缓存中的文件**
+
+    totalSize = photoGroup.value.selectedPhotosIds
+        .fold(0, (prev, id) => prev + (FileSizeCalculatorUtil.fileSizeCache[id] ?? 0));
+
+    // **2️⃣ 分批处理未缓存的文件**
+    const batchSize = 50;
+    for (int i = 0; i < uncasedIds.length; i += batchSize) {
+      if (photoGroup.value.selectedPhotosIds.isEmpty) {
+        photoGroup.value.selectedTotalSize.value = 0;
+        return;
       }
-    }).toList();
 
-    // 等待所有异步操作完成
-    await Future.wait(futures);
+      final batch = uncasedIds.skip(i).take(batchSize);
+      final sizes = await Future.wait(batch.map(FileSizeCalculatorUtil.getFileSize));
+
+      totalSize += sizes.fold(0, (sum, size) => sum + size);
 
-    // 更新选中文件的总大小
-    photoGroup.value.selectedTotalSize.value = totalSize;
+      // **再检查一次是否被清空,避免无意义计算**
+      if (photoGroup.value.selectedPhotosIds.isEmpty) {
+        photoGroup.value.selectedTotalSize.value = 0;
+        return;
+      }
 
-    // 打印调试信息
-    print("selectedFilesSize: ${photoGroup.value.selectedTotalSize.value}");
+      // **减少 UI 更新频率**
+      if (i % (batchSize * 2) == 0 || i + batchSize >= uncasedIds.length) {
+        photoGroup.value.selectedTotalSize.value = totalSize;
+      }
 
+      await Future.delayed(Duration.zero);
+    }
 
+    photoGroup.value.selectedTotalSize.value = totalSize; // 确保最终更新总大小
+    PhotoManager.clearFileCache();
   }
 
+
+
   void updateSelectedPhotosIds(String photoId, bool isSelected) {
     if (isSelected) {
       if (!photoGroup.value.selectedPhotosIds.contains(photoId)) {
@@ -157,8 +162,10 @@ class CalendarMonthController extends BaseController {
       photoGroup.value.selectedPhotosIds.remove(photoId);
     }
     photoGroup.value.isSelected.value =
-        photoGroup.value.selectedPhotosIds.length == photoGroup.value.images.length;
+        photoGroup.value.selectedPhotosIds.length ==
+            photoGroup.value.images.length;
   }
+
   clickImage(int imageIndex) {
     print("CalendarMonthController clickImage");
 
@@ -177,19 +184,19 @@ class CalendarMonthController extends BaseController {
     updateSelectedPhotosIds(photoId, isNowSelected);
 
     updateSelectedFilesSize();
-
   }
 
-
   String get selectedFilesSizeString {
-
     final double sizeInKB = photoGroup.value.selectedTotalSize.value;
 
-    if (sizeInKB >= 1024 * 1024) {  // 先检查最大单位(GB)
+    if (sizeInKB >= 1024 * 1024) {
+      // 先检查最大单位(GB)
       return "${(sizeInKB / (1024 * 1024)).toStringAsFixed(2)}GB";
-    } else if (sizeInKB >= 1024) {  // 然后检查MB
+    } else if (sizeInKB >= 1024) {
+      // 然后检查MB
       return "${(sizeInKB / 1024).toStringAsFixed(2)}MB";
-    } else {  // 最后是KB
+    } else {
+      // 最后是KB
       return "${sizeInKB.toStringAsFixed(2)}KB";
     }
   }

+ 2 - 1
lib/module/calendar/calendar_month_view.dart

@@ -56,11 +56,12 @@ class CalendarMonthPage extends BasePage<CalendarMonthController> {
                           "CalendarMonthPage buildBody ${controller.photoGroup
                               .value.images.length}");
                       return GridView.builder(
+
                           padding: EdgeInsets.symmetric(horizontal: 16.w),
                           scrollDirection: Axis.vertical,
                           // 设置为垂直方向滚动
                           physics: BouncingScrollPhysics(),
-                          cacheExtent: 1000,
+
                           gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                             crossAxisCount: 3, // 每行显示 3 个元素
                             mainAxisSpacing: 8.w, // 垂直间距

+ 35 - 21
lib/module/calendar/preview/calendar_preview_controller.dart

@@ -11,6 +11,7 @@ import 'package:wechat_assets_picker/wechat_assets_picker.dart';
 import '../../../data/repositories/user_repository.dart';
 import '../../../dialog/photo_delete_finish_dialog.dart';
 import '../../../dialog/photo_deleting_dialog.dart';
+import '../../../utils/file_size_calculator_util.dart';
 import '../../../utils/toast_util.dart';
 import '../../image_picker/image_picker_util.dart';
 import '../../people_photo/photo_group.dart';
@@ -260,35 +261,48 @@ class CalendarPreviewController extends BaseController
     }
 
     double totalSize = 0;
-    final selectedImages = photoGroup.value.images;
-
-    // 获取选中图片的大小(异步并行执行)
-    final futures = photoGroup.value.selectedPhotosIds.map((assetId) async {
-      if (ImagePickerUtil.fileSizeCache.containsKey(assetId)) {
-        totalSize += ImagePickerUtil.fileSizeCache[assetId]!;
-      } else {
-        final image = selectedImages.firstWhere((image) => image.id == assetId);
-        final file = await image.file;
-        if (file != null) {
-          final size = (await file.length()) / 1024; // 转换为KB
-          totalSize += size;
-          ImagePickerUtil.fileSizeCache[assetId] = size;
-        }
+    final uncasedIds = photoGroup.value.selectedPhotosIds
+        .where((id) => !FileSizeCalculatorUtil.fileSizeCache.containsKey(id))
+        .toSet();
+
+    // **1️⃣ 先处理缓存中的文件**
+
+    totalSize = photoGroup.value.selectedPhotosIds
+        .fold(0, (prev, id) => prev + (FileSizeCalculatorUtil.fileSizeCache[id] ?? 0));
+
+    // **2️⃣ 分批处理未缓存的文件**
+    const batchSize = 50;
+    for (int i = 0; i < uncasedIds.length; i += batchSize) {
+      if (photoGroup.value.selectedPhotosIds.isEmpty) {
+        photoGroup.value.selectedTotalSize.value = 0;
+        return;
       }
-    }).toList();
 
-    // 等待所有异步操作完成
-    await Future.wait(futures);
+      final batch = uncasedIds.skip(i).take(batchSize);
+      final sizes = await Future.wait(batch.map(FileSizeCalculatorUtil.getFileSize));
 
-    // 更新选中文件的总大小
-    photoGroup.value.selectedTotalSize.value = totalSize;
+      totalSize += sizes.fold(0, (sum, size) => sum + size);
 
-    // 打印调试信息
-    print("selectedFilesSize: ${photoGroup.value.selectedTotalSize.value}");
+      // **再检查一次是否被清空,避免无意义计算**
+      if (photoGroup.value.selectedPhotosIds.isEmpty) {
+        photoGroup.value.selectedTotalSize.value = 0;
+        return;
+      }
 
+      // **减少 UI 更新频率**
+      if (i % (batchSize * 2) == 0 || i + batchSize >= uncasedIds.length) {
+        photoGroup.value.selectedTotalSize.value = totalSize;
+      }
 
+      await Future.delayed(Duration.zero);
+    }
+
+    photoGroup.value.selectedTotalSize.value = totalSize; // 确保最终更新总大小
+    PhotoManager.clearFileCache();
   }
 
+
+
   void restoreSelections() {
 
 

+ 34 - 22
lib/module/calendar/selected_preview/calendar_selected_preview_controller.dart

@@ -9,6 +9,7 @@ import '../../../data/repositories/user_repository.dart';
 import '../../../dialog/photo_delete_finish_dialog.dart';
 import '../../../dialog/photo_deleting_dialog.dart';
 import '../../../router/app_pages.dart';
+import '../../../utils/file_size_calculator_util.dart';
 import '../../../utils/toast_util.dart';
 import '../../image_picker/image_picker_util.dart';
 import '../../people_photo/photo_group.dart';
@@ -134,37 +135,48 @@ class CalendarSelectedPreviewController extends BaseController {
     }
 
     double totalSize = 0;
-    final selectedImages = photoGroup.value.images;
+    final uncasedIds = photoGroup.value.selectedPhotosIds
+        .where((id) => !FileSizeCalculatorUtil.fileSizeCache.containsKey(id))
+        .toSet();
 
-    // 获取选中图片的大小(异步并行执行)
-    final futures = photoGroup.value.selectedPhotosIds.map((assetId) async {
-      if (ImagePickerUtil.fileSizeCache.containsKey(assetId)) {
-        totalSize += ImagePickerUtil.fileSizeCache[assetId]!;
-      } else {
-        final image = selectedImages.firstWhere((image) => image.id == assetId);
-        final file = await image.file;
-        if (file != null) {
-          final size = (await file.length()) / 1024; // 转换为KB
-          totalSize += size;
-          ImagePickerUtil.fileSizeCache[assetId] = size;
-        }
+    // **1️⃣ 先处理缓存中的文件**
+
+    totalSize = photoGroup.value.selectedPhotosIds
+        .fold(0, (prev, id) => prev + (FileSizeCalculatorUtil.fileSizeCache[id] ?? 0));
+
+    // **2️⃣ 分批处理未缓存的文件**
+    const batchSize = 50;
+    for (int i = 0; i < uncasedIds.length; i += batchSize) {
+      if (photoGroup.value.selectedPhotosIds.isEmpty) {
+        photoGroup.value.selectedTotalSize.value = 0;
+        return;
       }
-    }).toList();
 
-    // 等待所有异步操作完成
-    await Future.wait(futures);
+      final batch = uncasedIds.skip(i).take(batchSize);
+      final sizes = await Future.wait(batch.map(FileSizeCalculatorUtil.getFileSize));
+
+      totalSize += sizes.fold(0, (sum, size) => sum + size);
+
+      // **再检查一次是否被清空,避免无意义计算**
+      if (photoGroup.value.selectedPhotosIds.isEmpty) {
+        photoGroup.value.selectedTotalSize.value = 0;
+        return;
+      }
 
-    // 更新选中文件的总大小
-    photoGroup.value.selectedTotalSize.value = totalSize;
+      // **减少 UI 更新频率**
+      if (i % (batchSize * 2) == 0 || i + batchSize >= uncasedIds.length) {
+        photoGroup.value.selectedTotalSize.value = totalSize;
+      }
 
-    // 打印调试信息
-    debugPrint(
-        "selectedFilesSize: ${photoGroup.value.selectedTotalSize.value}");
+      await Future.delayed(Duration.zero);
+    }
 
-    // 清理缓存
+    photoGroup.value.selectedTotalSize.value = totalSize; // 确保最终更新总大小
     PhotoManager.clearFileCache();
   }
 
+
+
   void updateSelectedPhotosIds(String photoId, bool isSelected) {
     if (isSelected) {
       if (!photoGroup.value.selectedPhotosIds.contains(photoId)) {

+ 8 - 7
lib/module/image_picker/image_picker_util.dart

@@ -58,8 +58,8 @@ class ImagePickerUtil {
   static final Rx<int> peopleSize = 0.obs;
   static final Rx<int> similarPhotosSize = 0.obs;
 
-  // 用来缓存文件大小
-  static final Map<String, double> fileSizeCache = {};
+  // // 用来缓存文件大小
+  // static final Map<String, double> fileSizeCache = {};
 
   // 清除所有照片数据
   static void clearAllPhotos() {
@@ -310,7 +310,6 @@ class ImagePickerUtil {
 
     if (androidPhotos.isNotEmpty) return androidPhotos;
     // 选择相册
-
     final List<AssetPathEntity> albums = await PhotoManager.getAssetPathList(
       type: RequestType.image,
       filterOption: FilterOptionGroup(
@@ -324,13 +323,15 @@ class ImagePickerUtil {
       ),
     );
 
-    if (albums.isEmpty) return [];
 
-    print('相册数量:$albums');
-    // 获取图片资源
+
+
+    int assetCount = await albums.first.assetCountAsync;
+
+    debugPrint("ImagePickerUtil loadAssets assetCount $assetCount");
    androidPhotos = await albums.first.getAssetListPaged(
       page: 0,
-      size: 10000,
+      size: assetCount,
     );
 
     return androidPhotos;

+ 65 - 6
lib/module/people_photo/photo_group.dart

@@ -1,11 +1,18 @@
+import 'dart:io';
+import 'dart:isolate';
+
+import 'package:clean/utils/file_size_calculator_util.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/foundation.dart';
 import 'package:get/get.dart';
 import 'package:wechat_assets_picker/wechat_assets_picker.dart';
 
+import '../image_picker/image_picker_util.dart';
+
 class PhotoGroup {
   // 照片组的选择状态
   final RxBool isSelected;
 
-
   // 照片组中的图片列表
   final RxList<AssetEntity> images;
 
@@ -24,11 +31,12 @@ class PhotoGroup {
   // 照片组的月份
   final String? month;
 
+  // 整个 images 列表的总大小
+  RxDouble totalSize = 0.0.obs;
+
   // 获取已选中的图片数量
   int get selectedCount => selectedPhotosIds.length;
 
-
-
   // 构造函数
   PhotoGroup({
     required bool isSelected,
@@ -37,14 +45,65 @@ class PhotoGroup {
     this.month,
   })  : isSelected = isSelected.obs,
         images = images.obs,
-
-  selectedImages = RxList<bool>.filled(images.length, isSelected) {
+        selectedImages = RxList<bool>.filled(images.length, isSelected) {
     // 初始化已选中图片的唯一标识集合
     if (isSelected) {
       selectedPhotosIds.addAll(images.map((e) => e.id).toList());
     }
   }
 
+  Future<void> initTotalSize() async {
+
+    _calculateTotalSize();
+  }
+
+
+  Future<void> _calculateTotalSize() async {
+    if (images.isEmpty) {
+      this.totalSize.value = 0;
+      return;
+    }
+    double totalSize = 0;
+    final uncasedIds = images
+        .map((e) => e.id)
+        .where((id) => !FileSizeCalculatorUtil.fileSizeCache.containsKey(id))
+        .toSet();
+
+    // **1️⃣ 先处理缓存中的文件**
+    totalSize = images.fold(
+        0,
+        (prev, image) =>
+            prev + (FileSizeCalculatorUtil.fileSizeCache[image.id] ?? 0));
+
+    this.totalSize.value = totalSize;
+    // **2️⃣ 分批处理未缓存的文件**
+    const batchSize = 20;
+    for (int i = 0; i < uncasedIds.length; i += batchSize) {
+      if (images.isEmpty) {
+        this.totalSize.value = 0;
+        return;
+      }
+
+      final batch = uncasedIds.skip(i).take(batchSize);
+      final sizes =
+          await Future.wait(batch.map(FileSizeCalculatorUtil.getFileSize));
+
+      totalSize += sizes.fold(0, (sum, size) => sum + size);
+
+      // **再检查一次是否被清空,避免无意义计算**
+      if (images.isEmpty) {
+        this.totalSize.value = 0;
+        return;
+      }
+
+
+
+      await Future.delayed(Duration.zero);
+    }
+
+    this.totalSize.value = totalSize; // 确保最终更新总大小
+  }
+
   // 切换选择所有图片的状态
   void toggleSelectAll(bool value) {
     isSelected.value = value;
@@ -60,4 +119,4 @@ class PhotoGroup {
   bool isImageSelected(AssetEntity image) {
     return selectedPhotosIds.contains(image.id);
   }
-}
+}

+ 34 - 25
lib/module/photo_preview/photo_preview_controller.dart

@@ -25,6 +25,8 @@ import 'package:get/get.dart';
 import 'package:lottie/lottie.dart';
 import 'package:wechat_assets_picker/wechat_assets_picker.dart';
 
+import '../../utils/file_size_calculator_util.dart';
+
 class PhotoPreviewController extends BaseController
     with GetSingleTickerProviderStateMixin {
   Rx<CardSwiperController> cardSwiperController = CardSwiperController().obs;
@@ -185,48 +187,55 @@ class PhotoPreviewController extends BaseController
     selectedFileCount.value = selectedPhotosIds.length;
   }
 
+
   Future<void> updateSelectedFilesSize() async {
-    // 如果没有选中的文件,直接返回
     if (selectedPhotosIds.isEmpty) {
       selectedFilesSize.value = 0;
       return;
     }
 
     double totalSize = 0;
+    final uncasedIds = selectedPhotosIds.where((id) => !FileSizeCalculatorUtil.fileSizeCache.containsKey(id)).toSet();
 
-    // 创建一个 Future 列表,用于并行获取文件大小
-    List<Future<double>> sizeFutures = selectedPhotosIds.map((id) => getFileSize(id)).toList();
+    // **1️⃣ 先处理缓存中的文件**
 
-    // 等待所有文件大小计算完成
-    List<double> sizes = await Future.wait(sizeFutures);
+    totalSize = selectedPhotosIds.fold(0, (prev, id) => prev + (FileSizeCalculatorUtil.fileSizeCache[id] ?? 0));
 
-    // 计算总大小
-    totalSize = sizes.fold(0, (sum, size) => sum + size);
 
-    selectedFilesSize.value = totalSize; // 更新总大小
-    PhotoManager.clearFileCache();
-  }
+    // **2️⃣ 分批处理未缓存的文件**
+    const batchSize = 50;
+    for (int i = 0; i < uncasedIds.length; i += batchSize) {
+      if (selectedPhotosIds.isEmpty) {
+        selectedFilesSize.value = 0;
+        return;
+      }
 
-// 获取文件大小
-  Future<double> getFileSize(String assetId) async {
-    // 先检查缓存
-    if (ImagePickerUtil.fileSizeCache.containsKey(assetId)) {
-      return ImagePickerUtil.fileSizeCache[assetId]!;
-    }
+      final batch = uncasedIds.skip(i).take(batchSize);
+      final sizes = await Future.wait(batch.map(FileSizeCalculatorUtil.getFileSize));
 
-    final entity = await AssetEntity.fromId(assetId);
-    if (entity != null) {
-      final file = await entity.file;
-      if (file != null) {
-        double size = await file.length() / 1024; // 转换为 KB
-        ImagePickerUtil.fileSizeCache[assetId] = size; // 缓存文件大小
-        file.delete();
-        return size;
+      totalSize += sizes.fold(0, (sum, size) => sum + size);
+
+      // **再检查一次是否被清空,避免无意义计算**
+      if (selectedPhotosIds.isEmpty) {
+        selectedFilesSize.value = 0;
+        return;
       }
+
+      // **减少 UI 更新频率**
+      if (i % (batchSize * 2) == 0 || i + batchSize >= uncasedIds.length) {
+        selectedFilesSize.value = totalSize;
+      }
+
+      await Future.delayed(Duration.zero);
     }
-    return 0;
+
+    selectedFilesSize.value = totalSize; // 确保最终更新总大小
+    PhotoManager.clearFileCache();
   }
 
+
+
+
   void updateSelections(Set<String> selectedIds) {
     print(
         'PhotoPreviewController updateSelections photosType $photosType selectedIds $selectedIds');

+ 40 - 0
lib/utils/file_size_calculator_util.dart

@@ -0,0 +1,40 @@
+import 'package:flutter/cupertino.dart';
+import 'package:wechat_assets_picker/wechat_assets_picker.dart';
+import 'package:photo_manager/photo_manager.dart';
+
+class FileSizeCalculatorUtil {
+  static final Map<String, double> fileSizeCache = {};
+
+  /// 获取文件大小
+  static Future<double> getFileSize(String assetId) async {
+    if (fileSizeCache.containsKey(assetId)) {
+
+      return fileSizeCache[assetId]!;
+    }
+
+    final entity = await AssetEntity.fromId(assetId);
+    if (entity == null) return 0;
+
+    final file = await entity.file;
+    if (file == null) return 0;
+
+
+    double size = (await file.length()) / 1024;
+
+    if ( await file.exists()) {
+      try {
+        await file.delete();
+      } catch (e) {
+        debugPrint("Delete file error: $e");
+      }
+    } else {
+      debugPrint("File not exists: ${file.path}");
+    }
+
+    if (size <= 0) {
+      return 0;
+    }
+    fileSizeCache[assetId] = size;
+    return size;
+  }
+}