初始化

This commit is contained in:
谢宇宁
2026-09-15 15:44:13 +07:00
commit a23ba685e9
1065 changed files with 101122 additions and 0 deletions
@@ -0,0 +1,76 @@
import 'dart:io';
import 'dart:typed_data';
import 'package:path_provider/path_provider.dart';
import '../../debug_log.dart';
class ImageCacheDisk {
static Future<Uint8List?> get(String path) async {
var pathUrl = Uri.tryParse(path);
String fileName = "";
if (pathUrl?.path != null) {
fileName = pathUrl?.path.replaceAll("/", "") ?? "";
}
if (fileName.isNotEmpty) {
String filePath = "${await findSavePath()}/$fileName";
var imageFile = File(filePath);
if (imageFile.existsSync()) {
return imageFile.readAsBytes();
}
}
return null;
}
static void save(String path, List<int> fileData) async {
try {
if (fileData.length < 2048) {
return;
}
var pathUrl = Uri.tryParse(path);
String fileName = "";
if (pathUrl?.path != null) {
fileName = pathUrl?.path.replaceAll("/", "") ?? "";
}
if (fileName.isNotEmpty) {
String filePath = "${await findSavePath()}/$fileName";
var imageFile = File(filePath);
if (imageFile.existsSync()) {
imageFile.deleteSync();
}
imageFile.writeAsBytes(fileData, mode: FileMode.write);
}
} catch (e) {
debugLog("图片存储失败");
debugLog(e);
}
}
static Future<String> findSavePath() async {
final directory = Platform.isAndroid ? await getTemporaryDirectory() : await getTemporaryDirectory();
String saveDir = '${directory.path}/cacheImage';
Directory root = Directory(saveDir);
if (!root.existsSync()) {
debugLog(saveDir);
await root.create();
}
return saveDir;
}
static Future emptyCache() async {
try {
String saveDir = await findSavePath();
Directory root = Directory(saveDir);
if (root.existsSync()) {
await root.delete(recursive: true);
//showToast( "磁盘图片缓存清理成功");
// showToast( "磁盘图片缓存清理成功");
}
} catch (e) {
debugLog(e);
}
}
}
@@ -0,0 +1,157 @@
// ignore_for_file: unrelated_type_equality_checks, constant_identifier_names
import 'dart:typed_data';
import 'package:dio/dio.dart';
import 'package:flutter_image_compress/flutter_image_compress.dart';
import '../../debug_log.dart';
import 'image_cache_disk.dart';
class ImageCrypto {
static Future<Uint8List?> loadAndDecrypt(String path) async {
BaseOptions options = BaseOptions(
connectTimeout: const Duration(seconds: 20),
receiveTimeout: const Duration(seconds: 60),
responseType: ResponseType.bytes,
);
var url = path;
Response response;
try {
Uint8List? imageBytes = await ImageCacheDisk.get(url);
if (imageBytes == null) {
response = await Dio(options).get(url);
imageBytes = Uint8List.fromList(response.data);
ImageCacheDisk.save(url, response.data);
}
var decodeData = decryptImage(imageBytes);
if (!(url.contains('.gif') || url.contains('.GIF'))) {
decodeData = await compressList(decodeData);
}
return decodeData;
} on DioException catch (error) {
debugLog("图片加载失败:$error");
if (error.type == DioException.receiveTimeout) {
try {
response = await Dio(options).get(url);
var imageBytes = Uint8List.fromList(response.data);
ImageCacheDisk.save(url, response.data);
var decodeData = decryptImage(imageBytes);
return decodeData;
} catch (e) {
return null;
}
}
} catch (e) {
// debugLog("dio image error url: $url -> $e");
return null;
}
return null;
}
static final List<Uint8List> _featuresList = [
Uint8List.fromList([0xff, 0xd8, 0xff]), //jpg,jpeg
Uint8List.fromList([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]), //png
Uint8List.fromList([0x47, 0x49, 0x46]), //gif
];
/// 加密魔数头
static const encryptMagicNumber = [0x88, 0xA8, 0x30, 0xCB, 0x10, 0x76];
/// 加密密钥
static const ENCRYPT_KEY = 0xA3;
static const int _encryptedLen = 100; // //加密图片的数据长度
static final Uint8List _decryptKey = Uint8List.fromList('2019ysapp7527'.codeUnits); //加密key
static Uint8List? decryptImage(Uint8List? imgBytes) {
if (imgBytes?.isNotEmpty != true) {
return imgBytes;
}
var isAll = false;
for (var i = 0; i < encryptMagicNumber.length; i++) {
if (encryptMagicNumber[i] != imgBytes![i]) {
continue;
}
isAll = true;
}
// if (isAll) {
// imgBytes = xorBaseAllLength(imgBytes!);
// } else {
if (_isEncryptedImage(imgBytes!)) {
imgBytes = xorBaseLength(imgBytes, _decryptKey, _encryptedLen);
}
// }
return imgBytes;
}
static bool _isEncryptedImage(Uint8List imgBytes) {
bool isDecrypted = false;
int featuresLen = _featuresList.length;
for (int i = 0; i < featuresLen; i++) {
isDecrypted = false;
for (int j = 0; j < _featuresList[i].length; j++) {
if (_featuresList[i][j] != imgBytes[j]) {
isDecrypted = true;
break;
}
}
if (isDecrypted) {
continue;
} else {
break;
}
}
return isDecrypted;
}
static Uint8List xorBaseAllLength(Uint8List src) {
var index = -1;
int maxInt = double.maxFinite.toInt();
var dest = Uint8List.fromList(
src
.map((it) {
index++;
if (index < encryptMagicNumber.length && it == encryptMagicNumber[index]) {
return maxInt;
}
return it ^ ENCRYPT_KEY;
})
.where((element) => element != maxInt)
.toList(),
);
return dest;
}
static Uint8List xorBaseLength(Uint8List src, Uint8List key, int length) {
int srcLen = src.length;
int keyLen = key.length;
if (length > srcLen || length <= 0) {
length = srcLen;
}
for (var i = 0; i < length; i += keyLen) {
for (var j = 0; j < keyLen && i + j < length; j++) {
src[i + j] ^= key[j];
}
}
return src;
}
static Uint8List xor(Uint8List src, Uint8List key) {
return xorBaseLength(src, key, src.length);
}
static Future<Uint8List?> compressList(Uint8List? list) async {
if (list?.isNotEmpty == true) {
final result = await FlutterImageCompress.compressWithList(
list!,
quality: 92,
format: CompressFormat.webp,
);
//debugPrint("压缩前大小-----${list.length}");
//debugPrint("压缩后大小-----${result.length}");
return result;
}
return list;
}
}
@@ -0,0 +1,153 @@
import 'dart:typed_data';
import '../../debug_log.dart';
import 'image_crypto.dart';
typedef ImageCallback = void Function(String, Uint8List?);
class ImageManager {
// 工厂模式
factory ImageManager() => _getInstance();
static ImageManager get instance => _getInstance();
static ImageManager? _instance;
static int _taskCount = 0;
static final List<String> _taskQueue = [];
static final _taskCallbackMap = <String, List<ImageCallback>>{};
static final List<String> _taskNetOpQueue = [];
static int get maxTaskCount => 10;
int maxCacheSize = 3000;
ImageManager._internal();
static ImageManager _getInstance() {
_instance ??= ImageManager._internal();
return _instance!;
}
// 正在加载中的图片队列
//final Map<String, dynamic> _pendingImages = {};
// 缓存队列
final Map<String, Uint8List?> _cache = {};
final List<String> _cacheKey = [];
// 缓存数量上限(1000)
// final int _maximumSize = _kDefaultSize;
// 缓存容量上限 (100 MB)
// 清除所有缓存
void clearBySize({required int maxSize}) {
if (_cacheKey.length > maxSize) {
List<String> removeKey = [];
for (int i = 0; i < _cacheKey.length - maxSize; i++) {
removeKey.add(_cacheKey[i]);
}
for (int i = 0; i < removeKey.length; i++) {
_cacheKey.remove(removeKey[i]);
_cache.remove(removeKey[i]);
}
}
}
// 清除指定key对应的图片缓存
bool evict(String key) {
if (_cache[key] != null) {
_cache.remove(key);
return true;
}
return false;
}
Future<Uint8List?> loadImage(String? url) async {
if (url?.isNotEmpty != true) {
return null;
}
var imageBase = _cache[url];
if (imageBase != null) {
return imageBase;
}
imageBase = await ImageCrypto.loadAndDecrypt(url!);
if (imageBase != null && imageBase.length > 2048) {
_cache[url] = imageBase;
_cacheKey.add(url);
clearBySize(maxSize: maxCacheSize);
}
return imageBase;
}
void loadImageInQueue(String url, {ImageCallback? callback}) async {
if (url.isEmpty) {
if (callback != null) {
callback(url, null);
}
return;
}
Uint8List? imageBase = _cache[url];
if (imageBase != null) {
if (callback != null) {
callback(url, imageBase);
}
} else {
if (_taskCount > maxTaskCount) {
_taskQueue.remove(url);
_taskQueue.add(url);
if (callback != null) {
if (_taskCallbackMap[url] == null) {
_taskCallbackMap[url] = [callback];
} else {
_taskCallbackMap[url]?.remove(callback);
_taskCallbackMap[url]?.add(callback);
}
}
return;
}
if (_taskNetOpQueue.contains(url)) {
if (_taskCallbackMap[url] == null) {
_taskCallbackMap[url] == [callback];
} else {
if (callback != null) {
_taskCallbackMap[url]?.remove(callback);
_taskCallbackMap[url]?.add(callback);
}
}
return;
}
// 开始任务
_taskCount++;
_taskNetOpQueue.add(url);
try {
imageBase = await ImageCrypto.loadAndDecrypt(url);
if (imageBase != null) {
_cache[url] = imageBase;
_cacheKey.add(url);
clearBySize(maxSize: maxCacheSize);
}
} catch (e) {
debugLog(e);
}
if (callback != null) {
callback(url, imageBase);
}
_taskCallbackMap[url]?.forEach((element) {
element(url, imageBase);
});
_taskQueue.remove(url);
_taskCallbackMap.remove(url);
_taskNetOpQueue.remove(url);
_taskCount--;
for (int i = _taskCount; i <= maxTaskCount; i++) {
if (_taskQueue.isNotEmpty) {
String taskUrl = _taskQueue.first;
_taskQueue.remove(taskUrl);
try {
ImageManager.instance.loadImageInQueue(taskUrl);
} catch (e) {
debugLog(e);
}
}
}
}
}
}