import 'dart:io'; import 'package:dio/dio.dart'; import 'package:dio/io.dart'; import 'package:path_provider/path_provider.dart'; import 'package:path/path.dart'; import 'package:logger/web.dart'; /// OTA下载服务 /// 负责处理固件包的下载、文件管理等功能 class OtaDownloadService { late final Dio _dio; /// 构造函数 /// 初始化Dio实例并配置SSL证书处理 OtaDownloadService() { _dio = Dio(BaseOptions( connectTimeout: const Duration(seconds: 30), // 增加连接超时时间 receiveTimeout: Duration.zero, sendTimeout: const Duration(seconds: 30), // 增加发送超时时间 headers: {'Accept-Encoding': 'gzip'}, )); // 配置SSL证书验证 (_dio.httpClientAdapter as IOHttpClientAdapter).createHttpClient = () { final client = HttpClient(); // 处理SSL证书验证 client.badCertificateCallback = (X509Certificate cert, String host, int port) { // 在生产环境中,应该进行适当的证书验证 // 这里为了解决证书过期问题,暂时跳过验证 // 建议:在生产环境中实现更安全的证书验证逻辑 Logger().w('SSL证书验证跳过: $host:$port'); return true; }; return client; }; } /// 下载固件包 /// [downloadUrl] 下载地址 /// [onProgress] 下载进度回调 /// [onStatusUpdate] 状态更新回调 /// 返回下载文件的本地路径 /// 抛出异常如果下载失败 Future downloadFirmware({ required String downloadUrl, required Function(double) onProgress, required Function(String) onStatusUpdate, }) async { onStatusUpdate('准备下载固件包...'); final fileName = downloadUrl.substring(downloadUrl.lastIndexOf('/') + 1); const directoryName = "ota_upgrades"; // 获取下载目录 onStatusUpdate('正在获取下载目录...'); final Directory? downloadsDir = await getApplicationSupportDirectory(); if (downloadsDir == null) { throw FileSystemException('无法访问下载目录'); } final targetDir = Directory('${downloadsDir.path}/Download/$directoryName'); if (!await targetDir.exists()) { onStatusUpdate('创建下载目录...'); await targetDir.create(recursive: true); } final savePath = '${targetDir.path}/$fileName'; // 检查是否已存在同名文件 onStatusUpdate('检查本地文件...'); if (await _checkExistingFile(targetDir, fileName)) { onStatusUpdate('文件已存在,准备升级...'); return savePath; } // 清理旧文件 onStatusUpdate('清理旧文件...'); await _cleanOldFiles(targetDir); // 开始下载 onStatusUpdate('开始下载固件包...'); Logger().d('文件保存路径: $savePath'); final options = Options( receiveTimeout: Duration.zero, followRedirects: true, maxRedirects: 5, ); await _dio.download( downloadUrl, savePath, options: options, onReceiveProgress: (received, total) { if (total != -1) { final progress = received / total; onProgress(progress); onStatusUpdate('正在下载固件包... ${(progress * 100).toStringAsFixed(1)}%'); } }, ); // 验证文件是否下载成功 final file = File(savePath); if (!await file.exists()) { throw FileSystemException('文件下载失败,请检查写入权限'); } onStatusUpdate('下载完成,准备升级...'); return savePath; } /// 检查是否存在同名文件 /// [targetDir] 目标目录 /// [fileName] 文件名 /// 返回true表示文件已存在 Future _checkExistingFile(Directory targetDir, String fileName) async { try { final files = await targetDir.list().toList(); for (var entity in files) { if (await FileSystemEntity.isFile(entity.path)) { final name = basename(entity.path); if (name == fileName) { return true; } } } return false; } catch (e) { Logger().d('检查本地文件失败: $e'); return false; } } /// 清理旧文件 /// [targetDir] 目标目录 /// 抛出异常如果清理失败 Future _cleanOldFiles(Directory targetDir) async { try { final files = await targetDir.list().toList(); for (var entity in files) { if (await FileSystemEntity.isFile(entity.path)) { await entity.delete(); } } } catch (e) { throw Exception('删除旧文件失败: $e'); } } }