You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
157 lines
4.8 KiB
157 lines
4.8 KiB
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';
|
|
|
|
class DioManager {
|
|
static final DioManager _shared = DioManager._internal();
|
|
factory DioManager() => _shared;
|
|
|
|
late Dio dio;
|
|
// 根据地区获取服务器URL
|
|
String get baseUrl {
|
|
final countryCode = Get.deviceLocale?.countryCode?.toUpperCase();
|
|
final isChinaMainland = countryCode == 'CN';
|
|
|
|
if (!isChinaMainland) {
|
|
return dotenv.env['HW_SERVER_URL'] ?? dotenv.env['SERVER_URL'] ?? '';
|
|
} else {
|
|
return dotenv.env['SERVER_URL'] ?? '';
|
|
}
|
|
}
|
|
|
|
DioManager._internal() {
|
|
BaseOptions options = BaseOptions(
|
|
baseUrl: baseUrl,
|
|
contentType: Headers.jsonContentType,
|
|
responseType: ResponseType.json,
|
|
connectTimeout: const Duration(seconds: 30),
|
|
receiveTimeout: const Duration(seconds: 30),
|
|
);
|
|
|
|
dio = Dio(options);
|
|
|
|
dio.interceptors.add(AuthInterceptor());
|
|
}
|
|
|
|
// 请求,返回参数为 T
|
|
// method:请求方法,NWMethod.POST等
|
|
// path:请求地址
|
|
// params:请求参数
|
|
Future request<T>(
|
|
NWMethod method,
|
|
String path, {
|
|
Object? params,
|
|
Map<String, dynamic>? queryParameters,
|
|
}) async {
|
|
Response response = await dio.request(
|
|
path,
|
|
data: params,
|
|
queryParameters: queryParameters,
|
|
options: Options(method: nwMethodValues[method]),
|
|
);
|
|
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';
|
|
}
|
|
}
|
|
}
|
|
|