Browse Source

修改下载语音包

weicu
tanlongsheng 10 months ago
parent
commit
495dcd166d
  1. 100
      lib/data/services/network/dio_manager.dart
  2. 33
      lib/modules/wakeupvoice/controllers/wakeup_voice_controller.dart
  3. 4
      local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt

100
lib/data/services/network/dio_manager.dart

@ -1,10 +1,6 @@
import 'package:dio/dio.dart';
import 'package:dio/io.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:get/get.dart' hide Response;
import 'package:flutter/foundation.dart';
import 'dart:io';
import 'package:path/path.dart' as p;
import 'auth_interceptor.dart';
import 'nw_method.dart';
@ -58,100 +54,4 @@ class DioManager {
);
return response.data;
}
/// 下载二进制字节(支持可选忽略证书,仅限移动/桌面端)
Future<List<int>> downloadBytes(String url, {bool allowInsecure = false}) async {
// 使用独立 Dio,避免全局适配器被修改
final client = Dio(BaseOptions(
responseType: ResponseType.bytes,
followRedirects: true,
receiveTimeout: const Duration(seconds: 60),
connectTimeout: const Duration(seconds: 60),
));
client.interceptors.add(AuthInterceptor());
// 仅在非Web端尝试忽略证书
if (allowInsecure && !kIsWeb) {
client.httpClientAdapter = IOHttpClientAdapter(createHttpClient: () {
// ignore: unnecessary_new
final ioClient = new HttpClient();
ioClient.badCertificateCallback = (cert, host, port) => true;
return ioClient;
});
}
final resp = await client.get(url);
final status = resp.statusCode ?? 0;
if (status >= 200 && status < 300) {
// Dio v5 将 bytes 直接作为 List<int> 返回
final data = resp.data;
if (data is List<int>) return data;
// 某些平台可能返回 Uint8List
if (data is List) {
return List<int>.from(data);
}
throw Exception('下载返回格式异常');
}
throw Exception('下载失败($status)');
}
/// 按 .env 配置决定是否允许忽略证书
Future<List<int>> downloadBytesWithEnv(String url) async {
final allow = (dotenv.env['ALLOW_INSECURE_SSL'] ?? 'false').toLowerCase() == 'true';
return downloadBytes(url, allowInsecure: allow);
}
/// 使用 Dio 的 download API 下载到临时文件并读出字节
Future<List<int>> downloadBytesUsingDownload(String url, {bool allowInsecure = false}) async {
final client = Dio(BaseOptions(
followRedirects: true,
receiveTimeout: const Duration(seconds: 120),
connectTimeout: const Duration(seconds: 60),
));
client.interceptors.add(AuthInterceptor());
if (allowInsecure && !kIsWeb) {
client.httpClientAdapter = IOHttpClientAdapter(createHttpClient: () {
final ioClient = HttpClient();
ioClient.badCertificateCallback = (cert, host, port) => true;
return ioClient;
});
}
final tmpDir = await Directory.systemTemp.createTemp('voice_pkg_');
final baseName = _fileNameFromUrl(url);
final savePath = p.join(tmpDir.path, baseName);
await client.download(
url,
savePath,
options: Options(responseType: ResponseType.bytes),
);
final file = File(savePath);
final bytes = await file.readAsBytes();
// 清理临时文件
try {
await file.delete();
await tmpDir.delete(recursive: true);
} catch (_) {}
return bytes;
}
/// 读取 .env 决定是否忽略证书,使用 download API
Future<List<int>> downloadBytesUsingDownloadWithEnv(String url) async {
final allow = (dotenv.env['ALLOW_INSECURE_SSL'] ?? 'false').toLowerCase() == 'true';
return downloadBytesUsingDownload(url, allowInsecure: allow);
}
String _fileNameFromUrl(String url) {
try {
final clean = url.split('?').first;
final last = clean.split('/').last;
if (last.isEmpty) return 'voice.pkg';
return last;
} catch (_) {
return 'voice.pkg';
}
}
}

33
lib/modules/wakeupvoice/controllers/wakeup_voice_controller.dart

@ -1,11 +1,8 @@
import 'package:get/get.dart';
import 'package:dio/dio.dart';
import '../../../core/utils/logger.dart';
import '../../../data/services/ble_manager.dart';
import '../../../data/services/network/api.dart';
import '../../../data/services/network/dio_manager.dart';
import 'dart:convert';
import 'dart:typed_data';
import 'package:http/http.dart' as http;
class WakeupVoiceController extends GetxController {
// 是否启用唤醒词(原有本地词功能,可保留)
@ -256,23 +253,18 @@ class WakeupVoiceController extends GetxController {
Future<List<int>> _downloadVoiceBytes(String url) async {
try {
// 优先使用 Dio 的 download API(写入临时文件后读字节)
return await DioManager().downloadBytesUsingDownloadWithEnv(url);
final dio = Dio();
final response = await dio.get<List<int>>(
url,
options: Options(
responseType: ResponseType.bytes,
receiveTimeout: const Duration(seconds: 60),
),
);
return response.data ?? [];
} catch (e) {
// 降级为直接 GET bytes(同样带鉴权/证书忽略)
Logger.info('Dio download 失败,尝试直接 bytes GET: $e');
try {
return await DioManager().downloadBytesWithEnv(url);
} catch (e2) {
// 最后再回退到 http 包
Logger.info('Dio bytes GET 失败,回退 http: $e2');
final uri = Uri.parse(url);
final resp = await http.get(uri);
if (resp.statusCode == 200) {
return resp.bodyBytes;
}
throw Exception('下载失败(${resp.statusCode})');
}
Logger.error('下载语音包失败: $url', e);
return [];
}
}
@ -321,7 +313,6 @@ class WakeupVoiceController extends GetxController {
}
Future<void> saveChanges() async {
// TODO: 接入设备/SDK的唤醒词设置接口(本地词)
Get.snackbar('保存成功', '唤醒词设置已更新');
}
}

4
local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt

@ -883,8 +883,8 @@ object BleService {
// 获取主服务特征
val primarySvc = g.getService(BleConst.PRIMARY_SERVICE_UUID)
notifyChar = primarySvc?.getCharacteristic(BleConst.WakeupVoices_NOTIFY_UUID)
writeChar = primarySvc?.getCharacteristic(BleConst.WakeupVoices_WRITE_UUID)
notifyChar = primarySvc?.getCharacteristic(BleConst.NOTIFY_CHAR_UUID)
writeChar = primarySvc?.getCharacteristic(BleConst.WRITE_CHAR_UUID)
// 获取音频服务特征

Loading…
Cancel
Save