httpUploader.dart 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. import 'package:dio/dio.dart';
  2. import 'package:file_picker/file_picker.dart';
  3. import 'fileUtils.dart';
  4. const _show_debug_info = false;
  5. /// 上传状态枚举
  6. enum UploadStatus {
  7. uploading,
  8. completed,
  9. failed,
  10. }
  11. /// 上传结果
  12. class UploadResult<T> {
  13. final UploadStatus status;
  14. final String? filePath;
  15. final double? progress;
  16. final int? errorCode;
  17. final String? errorReason;
  18. final T? returnData;
  19. bool get isCompleted => status == UploadStatus.completed;
  20. bool get isUploading => status == UploadStatus.uploading;
  21. bool get isFailed => status == UploadStatus.failed;
  22. /// ----- error code -----
  23. static const int errorCodeError = -1;
  24. static const int errorCodeUserCancel = 1;
  25. static const int errorCodeFileIsNotExist = -2;
  26. /// ----- constructor -----
  27. UploadResult({
  28. required this.status,
  29. this.filePath,
  30. this.progress,
  31. this.errorCode,
  32. this.errorReason,
  33. this.returnData,
  34. });
  35. factory UploadResult.uploading(double progress) {
  36. if (progress < 0) { progress = 0; }
  37. else if (progress > 1) { progress = 1; }
  38. return UploadResult(
  39. status: UploadStatus.uploading,
  40. progress: progress,
  41. );
  42. }
  43. factory UploadResult.failed({
  44. int errorCode = errorCodeError,
  45. String? errorReason,
  46. }) {
  47. return UploadResult(
  48. status: UploadStatus.failed,
  49. errorCode: errorCode,
  50. errorReason: errorReason,
  51. );
  52. }
  53. factory UploadResult.completed({
  54. required String filePath,
  55. T? returnData,
  56. }) {
  57. return UploadResult(
  58. status: UploadStatus.completed,
  59. filePath: filePath,
  60. progress: 1.0,
  61. returnData: returnData,
  62. );
  63. }
  64. }
  65. /// -----------------------------------------------------------
  66. /// HttpUploader
  67. ///
  68. class HttpUploader {
  69. //上传
  70. static Future<UploadResult<T>> upload<T>({
  71. required String receiverUrl,
  72. String? filePath,
  73. String? fileName,
  74. Map<String, String>? headers,
  75. Map<String, dynamic>? extraData,
  76. void Function(UploadResult)? onProgress,
  77. }) async {
  78. // 选择文件
  79. String selectedFilePath;
  80. if (filePath != null) {
  81. if (await isFileExists(filePath)) {
  82. selectedFilePath = filePath;
  83. } else {
  84. final result = UploadResult<T>.failed(
  85. errorCode: UploadResult.errorCodeFileIsNotExist,
  86. errorReason: 'file is not exist',
  87. );
  88. if (onProgress != null) {
  89. onProgress(result);
  90. }
  91. return result;
  92. }
  93. } else {
  94. FilePickerResult? result = await FilePicker.platform.pickFiles();
  95. if (result == null) {
  96. final result = UploadResult<T>.failed(
  97. errorCode: UploadResult.errorCodeUserCancel,
  98. errorReason: 'Selection canceled by user',
  99. );
  100. if (onProgress != null) {
  101. onProgress(result);
  102. }
  103. return result;
  104. }
  105. PlatformFile file = result.files.first;
  106. selectedFilePath = file.path!;
  107. }
  108. FormData formData = FormData.fromMap({
  109. 'file': await MultipartFile.fromFile(selectedFilePath, filename: fileName),
  110. ...?extraData,
  111. });
  112. final dio = Dio();
  113. try {
  114. if (_show_debug_info) {
  115. print('┌────────────────────────────────────────────────────────────────────────────────────────');
  116. print('│ ⬆️ start upload file: $selectedFilePath');
  117. print('│ toServer: $receiverUrl');
  118. print('└────────────────────────────────────────────────────────────────────────────────────────');
  119. }
  120. final response = await dio.post<T>(
  121. receiverUrl,
  122. data: formData,
  123. options: Options(
  124. headers: headers,
  125. contentType: 'multipart/form-data',
  126. ),
  127. onSendProgress: (sent, total) {
  128. if (_show_debug_info) {
  129. print('upload: $sent / $total');
  130. }
  131. if (onProgress != null) {
  132. final progress = total != 0 ? sent / total : 0.0;
  133. onProgress(UploadResult<T>.uploading(progress));
  134. }
  135. },
  136. );
  137. if (_show_debug_info) {
  138. print('$filePath 上传完成: $response');
  139. }
  140. final result = UploadResult<T>.completed(
  141. filePath: selectedFilePath,
  142. returnData: response.data,
  143. );
  144. if (onProgress != null) {
  145. onProgress(result);
  146. }
  147. return result;
  148. } catch (e) {
  149. print('上传失败: $e');
  150. final result = UploadResult<T>.failed(
  151. errorReason: e.toString(),
  152. );
  153. if (onProgress != null) {
  154. onProgress(result);
  155. }
  156. return result;
  157. }
  158. }
  159. }