|
@@ -1,344 +0,0 @@
|
|
|
-import 'dart:convert';
|
|
|
-import 'dart:io';
|
|
|
-import 'package:shared/utils/encrypt_util.dart';
|
|
|
-import 'package:shared/utils/log_utils.dart';
|
|
|
-import 'package:synchronized/synchronized.dart';
|
|
|
-
|
|
|
-import 'package:path/path.dart' as path;
|
|
|
-
|
|
|
-import '../directory/directory_util.dart';
|
|
|
-
|
|
|
-/*
|
|
|
-
|
|
|
- // 存Json数据 - 写入key 与 json 即可,可以选择加入过期时间
|
|
|
- await fileCache.putJsonByKey(
|
|
|
- 'hutu',
|
|
|
- {
|
|
|
- "name": "hutu",
|
|
|
- "age": 35,
|
|
|
- },
|
|
|
- expiration: const Duration(hours: 1));
|
|
|
-
|
|
|
-
|
|
|
- // 取Json数据 - 根据key就能取到,如果没有或者过期则为null
|
|
|
- final json = await fileCache.getJsonByKey('hutu');
|
|
|
-
|
|
|
-
|
|
|
- // 存储基本数据类型
|
|
|
- await fileCache.putValueByKey('county', '凤凰');
|
|
|
- await fileCache.putValueByKey('age', 18);
|
|
|
-
|
|
|
- // 取出基本数据类型
|
|
|
- final county = await fileCache.getValueByKey<String>('county');
|
|
|
- Log.d('获取county:$county');
|
|
|
-
|
|
|
- final age = await fileCache.getValueByKey<int>('age');
|
|
|
- Log.d('获取age:$age');
|
|
|
-
|
|
|
- */
|
|
|
-class FileCacheManager {
|
|
|
- static const maxSizeInBytes = 500 * 1024 * 1024; // 最大限制 500M 文件缓存大小
|
|
|
- // static const maxSizeInBytes = 271360; // 最大限制 500M 文件缓存大小
|
|
|
- static const String _fileCategory = "app_file_cache";
|
|
|
- static final Lock _lock = Lock();
|
|
|
-
|
|
|
- //单例
|
|
|
- FileCacheManager._internal();
|
|
|
-
|
|
|
- static final FileCacheManager _singleton = FileCacheManager._internal();
|
|
|
-
|
|
|
- factory FileCacheManager() => _singleton;
|
|
|
-
|
|
|
- String? cachePath;
|
|
|
-
|
|
|
- // 初始化 - 获取到缓存路径
|
|
|
- Future _init() async {
|
|
|
- cachePath = DirectoryUtil.getTempPath(category: _fileCategory);
|
|
|
- if (cachePath != null) {
|
|
|
- //尝试异步创建自定义的文件夹
|
|
|
- await DirectoryUtil.createTempDir(category: _fileCategory);
|
|
|
- } else {
|
|
|
- throw Exception('DirectoryUtil 无法获取到Cache文件夹,可能 DirectoryUtil 没有初始化,请检查!');
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// 移除指定的 Key 对应的缓存文件
|
|
|
- Future<void> removeByKey(String key) async {
|
|
|
- if (cachePath == null) {
|
|
|
- await _init();
|
|
|
- }
|
|
|
-
|
|
|
- String path = '$cachePath/$key';
|
|
|
- final file = File(path);
|
|
|
- if (await file.exists()) {
|
|
|
- await file.delete();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// 移除全部的 Key 对应的缓存文件
|
|
|
- Future<void> removeAllKey() async {
|
|
|
- if (cachePath == null) {
|
|
|
- await _init();
|
|
|
- }
|
|
|
-
|
|
|
- Directory cacheDir = Directory(cachePath!);
|
|
|
- if (await cacheDir.exists()) {
|
|
|
- List<File> cacheFiles = cacheDir.listSync().whereType<File>().toList();
|
|
|
- if (cacheFiles.isNotEmpty) {
|
|
|
- for (File file in cacheFiles) {
|
|
|
- if (await file.exists()) {
|
|
|
- await file.delete();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 读取文件的全部Json数据
|
|
|
- Future<Map<String, dynamic>?> _readAllJsonFromFile(String key) async {
|
|
|
- String path = '$cachePath/$key';
|
|
|
- final file = File(path);
|
|
|
- if (await file.exists()) {
|
|
|
- String jsonString = await file.readAsString();
|
|
|
- final jsonData = jsonDecode(jsonString) as Map<String, dynamic>;
|
|
|
- return jsonData;
|
|
|
- }
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- // ============================= Json(对象) 类型 =========================================
|
|
|
-
|
|
|
- /// 添加Json数据到本地文件缓存
|
|
|
- Future<void> putJsonByKey(String key, Map<String, dynamic> jsonData, {Duration? expiration}) async {
|
|
|
- // 加锁
|
|
|
- await _lock.synchronized(() async {
|
|
|
- if (cachePath == null) {
|
|
|
- await _init();
|
|
|
- } else {
|
|
|
- Directory cacheDir = Directory(cachePath ?? '');
|
|
|
- if (!await cacheDir.exists()) {
|
|
|
- await DirectoryUtil.createTempDir(category: _fileCategory);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- //加密Key
|
|
|
- key = EncryptUtil.encodeMd5(key);
|
|
|
-
|
|
|
- final file = File('$cachePath/$key');
|
|
|
- Map<String, dynamic> existingData = {};
|
|
|
-
|
|
|
- //获取到已经存在的 Json
|
|
|
- if (await file.exists()) {
|
|
|
- final jsonString = await file.readAsString();
|
|
|
- existingData = jsonDecode(jsonString) as Map<String, dynamic>;
|
|
|
- }
|
|
|
-
|
|
|
- //存入现有的 key - value 缓存
|
|
|
- existingData[key] = {
|
|
|
- 'data': jsonData,
|
|
|
- 'timestamp': DateTime.now().millisecondsSinceEpoch,
|
|
|
- 'expiration': expiration?.inMilliseconds,
|
|
|
- };
|
|
|
-
|
|
|
- //转化为新的Json文本
|
|
|
- String newJsonString = jsonEncode(existingData);
|
|
|
-
|
|
|
- //检查限制的总大小并写入到文件
|
|
|
- checkAndWriteFile(file, newJsonString);
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- /// 从本都缓存文件读取Json数据
|
|
|
- Future<Map<String, dynamic>?> getJsonByKey(String key) async {
|
|
|
- if (cachePath == null) {
|
|
|
- await _init();
|
|
|
- } else {
|
|
|
- Directory cacheDir = Directory(cachePath ?? '');
|
|
|
- if (!await cacheDir.exists()) {
|
|
|
- await DirectoryUtil.createTempDir(category: _fileCategory);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- //加密Key
|
|
|
- key = EncryptUtil.encodeMd5(key);
|
|
|
-
|
|
|
- final jsonData = await _readAllJsonFromFile(key);
|
|
|
- if (jsonData == null) {
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- //取出对应 key 的Json文本
|
|
|
- final jsonEntry = jsonData[key] as Map<String, dynamic>?;
|
|
|
- if (jsonEntry == null || _isExpired(jsonEntry)) {
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- //返回去除过期时间之后的真正数据
|
|
|
- return jsonEntry['data'] as Map<String, dynamic>?;
|
|
|
- }
|
|
|
-
|
|
|
- // ============================= 基本数据类型 =========================================
|
|
|
-
|
|
|
- /// 添加基本数据类型的数据到本地文件缓存
|
|
|
- Future<void> putValueByKey<T>(String key, T value, {Duration? expiration}) async {
|
|
|
- // 加锁
|
|
|
- await _lock.synchronized(() async {
|
|
|
- if (cachePath == null) {
|
|
|
- await _init();
|
|
|
- } else {
|
|
|
- Directory cacheDir = Directory(cachePath ?? '');
|
|
|
- if (!await cacheDir.exists()) {
|
|
|
- await DirectoryUtil.createTempDir(category: _fileCategory);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- //加密Key
|
|
|
- key = EncryptUtil.encodeMd5(key);
|
|
|
-
|
|
|
- final file = File('$cachePath/$key');
|
|
|
- Map<String, dynamic> existingData = {};
|
|
|
-
|
|
|
- //获取到已经存在的 Json
|
|
|
- if (await file.exists()) {
|
|
|
- final jsonString = await file.readAsString();
|
|
|
- existingData = jsonDecode(jsonString) as Map<String, dynamic>;
|
|
|
- }
|
|
|
-
|
|
|
- //存入现有的 key - value 缓存
|
|
|
- existingData[key] = {
|
|
|
- 'data': value,
|
|
|
- 'timestamp': DateTime.now().millisecondsSinceEpoch,
|
|
|
- 'expiration': expiration?.inMilliseconds,
|
|
|
- };
|
|
|
-
|
|
|
- //转化为新的Json文本写入到文件
|
|
|
- String newJsonString = jsonEncode(existingData);
|
|
|
-
|
|
|
- //检查限制的总大小并写入到文件
|
|
|
- checkAndWriteFile(file, newJsonString);
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- /// 从本都缓存文件读取基本数据类型数据
|
|
|
- Future<T?> getValueByKey<T>(String key) async {
|
|
|
- if (cachePath == null) {
|
|
|
- await _init();
|
|
|
- } else {
|
|
|
- Directory cacheDir = Directory(cachePath ?? '');
|
|
|
- if (!await cacheDir.exists()) {
|
|
|
- await DirectoryUtil.createTempDir(category: _fileCategory);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- //加密Key
|
|
|
- key = EncryptUtil.encodeMd5(key);
|
|
|
-
|
|
|
- final jsonData = await _readAllJsonFromFile(key);
|
|
|
- if (jsonData == null) {
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- //取出对应 key 的Json文本
|
|
|
- final jsonEntry = jsonData[key] as Map<String, dynamic>?;
|
|
|
- if (jsonEntry == null || _isExpired(jsonEntry)) {
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- //返回去除过期时间之后的真正数据
|
|
|
- return jsonEntry['data'] as T?;
|
|
|
- }
|
|
|
-
|
|
|
- // 是否过期了
|
|
|
- bool _isExpired(Map<String, dynamic> jsonEntry) {
|
|
|
- final timestamp = jsonEntry['timestamp'] as int?;
|
|
|
- final expiration = jsonEntry['expiration'] as int?;
|
|
|
- if (timestamp != null && expiration != null) {
|
|
|
- final currentTime = DateTime.now().millisecondsSinceEpoch;
|
|
|
- return currentTime - timestamp > expiration;
|
|
|
- }
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- // 检查是否超过最大限制,并写入文件
|
|
|
- Future<void> checkAndWriteFile(File file, String jsonString) async {
|
|
|
- Directory cacheDir = Directory(cachePath!);
|
|
|
- List<File> cacheFiles = cacheDir.listSync().whereType<File>().toList();
|
|
|
-
|
|
|
- if (cacheFiles.isNotEmpty) {
|
|
|
- // 计算缓存文件夹的总大小
|
|
|
- int totalSizeInBytes = 0;
|
|
|
- for (File file in cacheFiles) {
|
|
|
- totalSizeInBytes += await file.length();
|
|
|
- }
|
|
|
-
|
|
|
- Log.d("totalSizeInBytes $totalSizeInBytes maxSizeInBytes:$maxSizeInBytes");
|
|
|
-
|
|
|
- //如果总大小超过限制,依次删除文件直到满足条件
|
|
|
- while (maxSizeInBytes > 0 && totalSizeInBytes > maxSizeInBytes) {
|
|
|
- File? fileToDelete;
|
|
|
- int oldestTimestamp = 0;
|
|
|
-
|
|
|
- for (File file in cacheDir.listSync().whereType<File>().toList()) {
|
|
|
- final key = path.basename(file.path);
|
|
|
- //取出全部的 Json 文本与对象
|
|
|
- final jsonString = await file.readAsString();
|
|
|
- final jsonData = jsonDecode(jsonString) as Map<String, dynamic>?;
|
|
|
- if (jsonData == null) {
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- //取出对应 key 的 Json 对象
|
|
|
- final jsonEntry = jsonData[key] as Map<String, dynamic>?;
|
|
|
- if (jsonEntry == null || _isExpired(jsonEntry)) {
|
|
|
- fileToDelete = file;
|
|
|
- Log.d("FileCacheManager 找到过期的文件$file");
|
|
|
- break;
|
|
|
-
|
|
|
- } else {
|
|
|
-
|
|
|
- // 最进最少操作的时间戳
|
|
|
- final timestamp = (await file.lastModified()).millisecondsSinceEpoch;
|
|
|
- if (oldestTimestamp == 0) {
|
|
|
- oldestTimestamp = timestamp;
|
|
|
- fileToDelete = file;
|
|
|
- }
|
|
|
-
|
|
|
- if (timestamp < oldestTimestamp) {
|
|
|
- oldestTimestamp = timestamp;
|
|
|
- fileToDelete = file;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- //遍历文件结束之后需要删除处理的逻辑
|
|
|
- Log.d("FileCacheManager 需要删除的文件:$fileToDelete");
|
|
|
- if (fileToDelete != null && await fileToDelete.exists()) {
|
|
|
- await fileToDelete.delete();
|
|
|
- break;
|
|
|
- } else {
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- } //结束While循环
|
|
|
-
|
|
|
- Log.d("写入的路径:$file");
|
|
|
- //最后写入文件
|
|
|
- await file.writeAsString(jsonString);
|
|
|
- } else {
|
|
|
- // 如果是空文件夹,直接写即可
|
|
|
- await file.writeAsString(jsonString);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- // 根据文件路径计算文件大小
|
|
|
- Future<int> _calculateSize(File file) async {
|
|
|
- if (await file.exists()) {
|
|
|
- return await file.length();
|
|
|
- }
|
|
|
- return 0;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-//可以用 fileCache 直接使用更方便
|
|
|
-var fileCache = FileCacheManager();
|