yx_oss_flutter/lib/src/utils/local_resume_storage.dart

197 lines
4.8 KiB
Dart

import 'dart:convert';
import 'dart:io';
import '../interfaces/resume_storage.dart';
// 注意:这个实现需要 path_provider 包
// 在实际使用时需要在 pubspec.yaml 中添加 path_provider: ^2.1.0
/// 基于本地文件的断点续传存储实现
class LocalResumeStorage implements ResumeStorage {
static const String _storageDir = 'yx_oss_resume';
static const String _fileExtension = '.json';
Directory? _cacheDirectory;
/// 获取缓存目录
Future<Directory> get cacheDirectory async {
if (_cacheDirectory != null) {
return _cacheDirectory!;
}
// 简化实现:使用临时目录,避免依赖 path_provider
// 在实际集成时可以替换为 getApplicationDocumentsDirectory()
final tempDir = Directory.systemTemp;
_cacheDirectory = Directory('${tempDir.path}/$_storageDir');
// 确保目录存在
if (!await _cacheDirectory!.exists()) {
await _cacheDirectory!.create(recursive: true);
}
return _cacheDirectory!;
}
/// 获取任务文件路径
Future<File> _getTaskFile(String taskId) async {
final dir = await cacheDirectory;
return File('${dir.path}/$taskId$_fileExtension');
}
@override
Future<void> saveResumeInfo(
String taskId, Map<String, dynamic> resumeInfo) async {
try {
final file = await _getTaskFile(taskId);
final jsonString = jsonEncode(resumeInfo);
await file.writeAsString(jsonString);
} catch (e) {
throw Exception('Failed to save resume info for task $taskId: $e');
}
}
@override
Future<Map<String, dynamic>?> loadResumeInfo(String taskId) async {
try {
final file = await _getTaskFile(taskId);
if (!await file.exists()) {
return null;
}
final jsonString = await file.readAsString();
return jsonDecode(jsonString) as Map<String, dynamic>;
} catch (e) {
// 文件损坏或格式错误时删除文件
try {
final file = await _getTaskFile(taskId);
if (await file.exists()) {
await file.delete();
}
} catch (_) {
// 忽略删除错误
}
return null;
}
}
@override
Future<void> deleteResumeInfo(String taskId) async {
try {
final file = await _getTaskFile(taskId);
if (await file.exists()) {
await file.delete();
}
} catch (e) {
// 忽略删除错误,可能文件已不存在
}
}
@override
Future<List<String>> getAllTaskIds() async {
try {
final dir = await cacheDirectory;
if (!await dir.exists()) {
return [];
}
final files = await dir.list().toList();
final taskIds = <String>[];
for (final file in files) {
if (file is File && file.path.endsWith(_fileExtension)) {
final fileName = file.path.split('/').last;
final taskId =
fileName.substring(0, fileName.length - _fileExtension.length);
taskIds.add(taskId);
}
}
return taskIds;
} catch (e) {
return [];
}
}
@override
Future<void> clearAll() async {
try {
final dir = await cacheDirectory;
if (await dir.exists()) {
await dir.delete(recursive: true);
}
_cacheDirectory = null; // 重置缓存目录
} catch (e) {
// 忽略删除错误
}
}
@override
Future<int> getStorageSize() async {
try {
final dir = await cacheDirectory;
if (!await dir.exists()) {
return 0;
}
int totalSize = 0;
final files = await dir.list().toList();
for (final file in files) {
if (file is File) {
final stat = await file.stat();
totalSize += stat.size;
}
}
return totalSize;
} catch (e) {
return 0;
}
}
/// 清理过期的断点续传信息
///
/// [maxAge] 最大保留时间(天数)
Future<void> cleanupExpiredTasks(int maxAge) async {
try {
final dir = await cacheDirectory;
if (!await dir.exists()) {
return;
}
final cutoffTime = DateTime.now().subtract(Duration(days: maxAge));
final files = await dir.list().toList();
for (final file in files) {
if (file is File && file.path.endsWith(_fileExtension)) {
final stat = await file.stat();
if (stat.modified.isBefore(cutoffTime)) {
try {
await file.delete();
} catch (_) {
// 忽略删除错误
}
}
}
}
} catch (e) {
// 忽略清理错误
}
}
/// 获取任务的文件大小
Future<int> getTaskFileSize(String taskId) async {
try {
final file = await _getTaskFile(taskId);
if (await file.exists()) {
final stat = await file.stat();
return stat.size;
}
return 0;
} catch (e) {
return 0;
}
}
}