file_stream_util.dart 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. import 'dart:io';
  2. import 'dart:convert';
  3. import 'package:crypto/crypto.dart';
  4. import 'package:dio/io.dart';
  5. import 'package:path/path.dart' as path;
  6. import 'package:dio/dio.dart';
  7. import 'developer/ix_developer_tools.dart';
  8. class FileStreamUtils {
  9. static const int defaultBufferSize = 64 * 1024;
  10. static final Dio dio = Dio();
  11. static setProxy(String proxy) {
  12. dio.httpClientAdapter = IOHttpClientAdapter(
  13. createHttpClient: () {
  14. final client = HttpClient();
  15. client.findProxy = (uri) => proxy;
  16. client.badCertificateCallback =
  17. (X509Certificate cert, String host, int port) => true;
  18. return client;
  19. },
  20. );
  21. }
  22. /// 读取文件(支持本地文件和网络URL)
  23. /// [path] 文件路径或URL
  24. /// [encoding] 文件编码,默认UTF8
  25. /// [onProgress] 读取进度回调
  26. static Future<FileReadResult> readTextFile({
  27. required String path,
  28. Encoding encoding = utf8,
  29. void Function(double progress)? onProgress,
  30. }) async {
  31. if (path.startsWith('http://') || path.startsWith('https://')) {
  32. return _readFromUrl(
  33. url: path,
  34. encoding: encoding,
  35. onProgress: onProgress,
  36. );
  37. } else {
  38. return _readFromFile(
  39. filePath: path,
  40. encoding: encoding,
  41. onProgress: onProgress,
  42. );
  43. }
  44. }
  45. /// 从URL读取
  46. static Future<FileReadResult> _readFromUrl({
  47. required String url,
  48. required Encoding encoding,
  49. void Function(double progress)? onProgress,
  50. }) async {
  51. try {
  52. // 添加talker日志
  53. dio.interceptors.add(SimpleNoSignApiMonitorInterceptor());
  54. final response = await dio.get<List<int>>(
  55. url,
  56. options: Options(
  57. responseType: ResponseType.bytes,
  58. followRedirects: true,
  59. ),
  60. onReceiveProgress: (received, total) {
  61. if (total != -1 && onProgress != null) {
  62. onProgress(received / total);
  63. }
  64. },
  65. );
  66. if (response.data == null) {
  67. throw Exception('No data received');
  68. }
  69. final bytes = response.data!;
  70. final content = encoding.decode(bytes);
  71. final md5Hash = md5.convert(bytes).toString();
  72. return FileReadResult(
  73. content: content,
  74. md5Hash: md5Hash,
  75. fileName: path.basename(url),
  76. fileSize: bytes.length,
  77. );
  78. } catch (e) {
  79. throw Exception('Error downloading file: $e');
  80. }
  81. }
  82. /// 从本地文件读取
  83. static Future<FileReadResult> _readFromFile({
  84. required String filePath,
  85. required Encoding encoding,
  86. void Function(double progress)? onProgress,
  87. }) async {
  88. try {
  89. final file = File(filePath);
  90. if (!await file.exists()) {
  91. throw FileSystemException('File not found', filePath);
  92. }
  93. final fileSize = await file.length();
  94. var bytesRead = 0;
  95. final List<int> allBytes = [];
  96. final StringBuffer content = StringBuffer();
  97. final stream = file.openRead();
  98. await for (var data in stream) {
  99. allBytes.addAll(data);
  100. content.write(encoding.decode(data));
  101. bytesRead += data.length;
  102. if (onProgress != null) {
  103. onProgress(bytesRead / fileSize);
  104. }
  105. }
  106. final md5Hash = md5.convert(allBytes).toString();
  107. return FileReadResult(
  108. content: content.toString(),
  109. md5Hash: md5Hash,
  110. fileName: path.basename(filePath),
  111. fileSize: fileSize,
  112. );
  113. } catch (e) {
  114. throw FileSystemException('Error reading file: $e', filePath);
  115. }
  116. }
  117. /// 验证文件或URL的MD5
  118. static Future<bool> verifyMd5({
  119. required String path,
  120. required String expectedMd5,
  121. void Function(double progress)? onProgress,
  122. }) async {
  123. try {
  124. final result = await readTextFile(path: path, onProgress: onProgress);
  125. return result.md5Hash.toLowerCase() == expectedMd5.toLowerCase();
  126. } catch (e) {
  127. return false;
  128. }
  129. }
  130. /// 获取文件或URL的MD5
  131. static Future<String?> getMd5({
  132. required String path,
  133. void Function(double progress)? onProgress,
  134. }) async {
  135. try {
  136. final result = await readTextFile(path: path, onProgress: onProgress);
  137. return result.md5Hash;
  138. } catch (e) {
  139. return null;
  140. }
  141. }
  142. }
  143. /// 文件读取结果
  144. class FileReadResult {
  145. final String content; // 文件内容
  146. final String md5Hash; // MD5值
  147. final String fileName; // 文件名
  148. final int fileSize; // 文件大小
  149. FileReadResult({
  150. required this.content,
  151. required this.md5Hash,
  152. required this.fileName,
  153. required this.fileSize,
  154. });
  155. @override
  156. String toString() {
  157. return 'FileReadResult{fileName: $fileName, fileSize: $fileSize, md5Hash: $md5Hash}';
  158. }
  159. }