海哥 发表于 2025-1-7 19:55:36

Flutter中的网络请求图片存储为缓存,与定制删除本地缓存

Flutter中的网络请求图片存储为缓存,与定制删除本地缓存
1:封装请求图片函数
2:访问的图片都会转为本地缓存,当相同的请求url,会在本地调用图片
3:本地缓存管理【windows与andriod已经测试】【有页面】【有调用案例】
4:删除本地缓存
清理缓存页面(下方代码中已包括)
https://i-blog.csdnimg.cn/direct/09748138705444a38574f734693567c2.png
https://i-blog.csdnimg.cn/direct/10231ff31f2f475abf873ac83ebc7fc2.png
windows中显示图片-----------安卓中显示图片
这里还没有进行优化图片显示的宽高,圆角,请自行设置
https://i-blog.csdnimg.cn/direct/c17733a638ff48cf81662740b6a5e91b.png
打印日志(显示图片请求的获取过程与报错原因)

https://i-blog.csdnimg.cn/direct/e824fdf76a2a44079c4517866211c20c.png
TuPianJiaZai 图片加载工具使用教程

注意事项


[*]imageUrl 可以为 null,此时会显示空白
[*]图片会自动缓存到本地
[*]支持自动重试3次
[*]默认有加载动画和错误提示
[*]支持所有尺度图片格式
现实应用场景


[*]商品展示卡片
[*]用户头像
[*]图片列表
[*]背景图片
[*]Banner图片
1. 基本用法

1.1导入文件
import '../utils/get_images/tupianjiazai.dart';
TuPianJiaZai.jiazaiTupian(
                        imageUrl: product.image,
                        width: double.infinity,
                        height: 200,
                        fit: BoxFit.cover,
)
2. 完整参数说明

TuPianJiaZai.jiazaiTupian(
                         // 必需参数
                        imageUrl: String?, // 图片URL,可以为null
                        // 可选参数
                        width: double?, // 显示宽度
                        height: double?, // 显示高度
                        fit: BoxFit, // 图片填充方式,默认BoxFit.cover
                        cacheWidth: int?, // 缓存图片宽度,用于优化内存
                        cacheHeight: int?, // 缓存图片高度,用于优化内存
                        placeholder: Widget?, // 加载时显示的占位Widget
                        errorWidget: Widget?, // 加载失败时显示的Widget
)
3. 使用案例

3.1 基础加载

TuPianJiaZai.jiazaiTupian(
                        imageUrl: 'https://example.com/image.jpg',
                        width: 200,
                        height: 200,
)
3.2 自定义占位图和错误图

TuPianJiaZai.jiazaiTupian(
                        imageUrl: imageUrl,
                        width: 300,
                        height: 200,
                        placeholder: const Center(
                        child: CircularProgressIndicator(),
                        ),
errorWidget: const Center(
                        child: Column(
                                        mainAxisAlignment: MainAxisAlignment.center,
                                        children: ,
                                    ),
                     ),
)
3.3 列表项中使用

ListView.builder(
itemBuilder: (context, index) {
return TuPianJiaZai.jiazaiTupian(
imageUrl: imageUrls,
height: 150,
fit: BoxFit.cover,
cacheWidth: 600, // 优化缓存大小
cacheHeight: 400,
);
},
)
请自行在\lib\utils\get_images\文件夹中创建一下设置

D:\F\luichun\lib\utils\get_images\huancunguanli.dart
import 'dart:io';
import 'dart:typed_data';
import 'package:path_provider/path_provider.dart';
import 'package:crypto/crypto.dart';
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:synchronized/synchronized.dart';
import 'logger.dart';// 使用统一的日志管理器
import '../env_config.dart';// 本地进行开发时,使用 会对 localhost:10005 进行请求,但是安卓模拟器需要把localhost转换为 10.0.2.2

/// 完整工作流程:
/// 1.应用启动 -> 初始化缓存目录
/// 2.请求图片 -> 检查缓存 -> 返回缓存或null
/// 3.下载图片 -> 保存图片 -> 更新映射关系
/// 4.定期维护 -> 清理缓存/计算大小

/// 图片缓存管理器
/// 用于管理图片的本地缓存,减少重复的网络请求
class HuanCunGuanLi {
/// 单例模式
///   使用工厂构造函数确保全局只有一个缓存管理器实例
///   避免重复创建缓存目录和资源浪费
static final HuanCunGuanLi _instance = HuanCunGuanLi._internal();

/// 缓存目录
Directory? _cacheDir;

/// 初始化锁
final _lock = Lock();

/// 初始化标志
bool _isInitialized = false;

/// 持久化存储的键名
static const String _prefKey = 'image_cache_urls';

// 工厂构造函数
factory HuanCunGuanLi() {
    return _instance;
}

// 私有构造函数
HuanCunGuanLi._internal();

/// 确保已初始化
Future<void> _ensureInitialized() async {
    if (_isInitialized) return;// 快速检查
   
    await _lock.synchronized(() async {
      if (_isInitialized) return;// 双重检查
      await init();
    });
}

/// 初始化缓存目录
Future<void> init() async {
    try {
      final appDir = await getApplicationDocumentsDirectory();
      final cacheDir = Directory('${appDir.path}/image_cache');
      
      if (!await cacheDir.exists()) {
      await cacheDir.create(recursive: true);
      }
      
      _cacheDir = cacheDir;
      _isInitialized = true;
      
      if (EnvConfig.isDevelopment) {
      ImageLogger.logCacheInfo('缓存系统初始化完成: ${_cacheDir!.path}');
      }
    } catch (e) {
      ImageLogger.logCacheError('缓存系统初始化失败', error: e);
      rethrow;
    }
}

/// 3异步获取缓存图片
/// 参数:
///   url: 图片的网络地址
/// 返回:
///   Uint8List?: 图片的二进制数据,不存在时返回null
/// 流程:
///   1. 根据URL生成缓存键
///   2. 查找本地缓存文件
///   3. 返回缓存数据或null
Future<Uint8List?> huoquTupian(String url) async {
    await _ensureInitialized();
    try {
      final cacheKey = _shengchengKey(url);
      final cacheFile = File('${_cacheDir!.path}/$cacheKey');
      
      if (await cacheFile.exists()) {
      ImageLogger.logCacheDebug('从缓存加载图片', {'url': url});
      return await cacheFile.readAsBytes();
      }
      return null;
    } catch (e) {
      ImageLogger.logCacheError('读取缓存图片失败', error: e);
      return null;
    }
}

/// 异步保存图片到缓存
/// 图片URL
/// 图片二进制数据
Future<void> baocunTupian(String url, Uint8List imageBytes) async {
    await _ensureInitialized();
    final cacheKey = _shengchengKey(url);
    final cacheFile = File('${_cacheDir!.path}/$cacheKey');
    await cacheFile.writeAsBytes(imageBytes);
    await _baocunURLyingshe(url, cacheKey);
}

/// 生成缓存键
/// 使用MD5加密URL生成唯一标识
String _shengchengKey(String url) {
    final bytes = utf8.encode(url);
    final digest = md5.convert(bytes);
    return digest.toString();
}

/// 4. URL 映射管理:
/// 保存URL映射关系
/// 实现:
///   1. 获取SharedPreferences实例
///   2. 读取现有映射
///   3. 更新映射关系
///   4. 序列化并保存
///   使用 SharedPreferences 持久化存储 URL 映射关系
///   JSON 序列化保存映射数据
///   异步操作避免阻塞主线程
/// 保存URL映射关系
Future<void> _baocunURLyingshe(String url, String cacheKey) async {
    final prefs = await SharedPreferences.getInstance();
    final Map<String, String> urlMap = await _huoquURLyingshe();
    urlMap = cacheKey;
    await prefs.setString(_prefKey, jsonEncode(urlMap));
}

/// 获取URL映射关系
Future<Map<String, String>> _huoquURLyingshe() async {
    final prefs = await SharedPreferences.getInstance();
    final String? mapJson = prefs.getString(_prefKey);
    if (mapJson != null) {
      return Map<String, String>.from(jsonDecode(mapJson));
    }
    return {};
}
/// 5.缓存清理功能:
/// 清除所有缓存
/// 使用场景:
///   1. 应用清理存储空间
///   2. 图片资源更新
///   3. 缓存出现问题时重置
/// 递归删除缓存目录
/// 清除 URL 映射数据
/// 清除所有缓存
Future<void> qingchuHuancun() async {
    await _cacheDir!.delete(recursive: true);
    await _cacheDir!.create();
    final prefs = await SharedPreferences.getInstance();
    await prefs.remove(_prefKey);
}

   ///6 .缓存大小计算:
   ///- 异步遍历缓存目录
   /// 累计所有文件大小
   /// 使用 Stream 处理大目录
/// 获取缓存大小(字节)
Future<int> huoquHuancunDaxiao() async {
    int size = 0;
    await for (final file in _cacheDir!.list()) {
      if (file is File) {
      size += await file.length();
      }
    }
    return size;
}
}
D:\F\luichun\lib\utils\get_images\logger.dart
import 'package:logger/logger.dart';

/// 图片加载系统的日志管理器
class ImageLogger {
static final Logger _logger = Logger(
    printer: PrettyPrinter(
      methodCount: 0,
      errorMethodCount: 8,
      lineLength: 120,
      colors: true,
      printEmojis: true,
      dateTimeFormat: DateTimeFormat.onlyTimeAndSinceStart,
    ),
);

// 缓存系统日志
static void logCacheInfo(String message) {
    _logger.i('
页: [1]
查看完整版本: Flutter中的网络请求图片存储为缓存,与定制删除本地缓存