Flutter中的网络请求图片存储为缓存,与定制删除本地缓存
1:封装请求图片函数
2:访问的图片都会转为本地缓存,当相同的请求url,会在本地调用图片
3:本地缓存管理【windows与andriod已经测试】【有页面】【有调用案例】
4:删除本地缓存
清理缓存页面(下方代码中已包括)
windows中显示图片-----------安卓中显示图片
这里还没有进行优化图片显示的宽高,圆角,请自行设置
打印日志(显示图片请求的获取过程与报错原因)
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: [Icon(Icons.error),Text('加载失败'),],
- ),
- ),
- )
复制代码 3.3 列表项中使用
- ListView.builder(
- itemBuilder: (context, index) {
- return TuPianJiaZai.jiazaiTupian(
- imageUrl: imageUrls[index],
- 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] 图片URL
- /// [imageBytes] 图片二进制数据
- 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[url] = 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
[code]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(' |