瀏覽代碼

上传文件dio封装。

PC\19500 2 周之前
父節點
當前提交
c4acde9c97
共有 1 個文件被更改,包括 253 次插入6 次删除
  1. 253 6
      lib/core/network/dio_client.dart

+ 253 - 6
lib/core/network/dio_client.dart

@@ -1,3 +1,4 @@
+import 'dart:io';
 import 'package:dio/dio.dart' hide LogInterceptor;
 import 'package:sino_med_cloud/core/constants/api_constants.dart';
 import 'package:sino_med_cloud/core/network/interceptors/auth_interceptor.dart';
@@ -85,7 +86,7 @@ class DioClient {
         response.data as Map<String, dynamic>,
         fromJsonT,
       );
-    } on DioException catch (e) {
+    } on DioException {
       // 错误已由 ErrorInterceptor 处理,直接抛出
       rethrow;
     }
@@ -112,7 +113,7 @@ class DioClient {
         response.data as Map<String, dynamic>,
         fromJsonT,
       );
-    } on DioException catch (e) {
+    } on DioException {
       // 错误已由 ErrorInterceptor 处理,直接抛出
       rethrow;
     }
@@ -154,7 +155,7 @@ class DioClient {
         response.data as Map<String, dynamic>,
         fromJsonT,
       );
-    } on DioException catch (e) {
+    } on DioException {
       // 错误已由 ErrorInterceptor 处理,直接抛出
       rethrow;
     }
@@ -192,7 +193,7 @@ class DioClient {
         response.data as Map<String, dynamic>,
         fromJsonT,
       );
-    } on DioException catch (e) {
+    } on DioException {
       // 错误已由 ErrorInterceptor 处理,直接抛出
       rethrow;
     }
@@ -234,7 +235,7 @@ class DioClient {
         response.data as Map<String, dynamic>,
         fromJsonT,
       );
-    } on DioException catch (e) {
+    } on DioException {
       // 错误已由 ErrorInterceptor 处理,直接抛出
       rethrow;
     }
@@ -276,7 +277,253 @@ class DioClient {
         response.data as Map<String, dynamic>,
         fromJsonT,
       );
-    } on DioException catch (e) {
+    } on DioException {
+      // 错误已由 ErrorInterceptor 处理,直接抛出
+      rethrow;
+    }
+  }
+
+  // ==================== 文件上传 ====================
+
+  /// 单文件上传
+  /// 
+  /// [path] 请求路径(相对于 baseUrl)
+  /// [fromJsonT] JSON 转换为 T 类型的函数
+  /// [file] 要上传的文件
+  /// [fileKey] 文件字段名,默认为 'file'
+  /// [fileName] 文件名,如果不提供则使用文件的原始名称
+  /// [fields] 其他表单字段(可选)
+  /// [queryParameters] 查询参数
+  /// [options] 请求选项
+  /// [onSendProgress] 上传进度回调
+  /// 
+  /// 返回 [BaseCommonResponse] 对象
+  /// 
+  /// 示例:
+  /// ```dart
+  /// final file = File('/path/to/image.jpg');
+  /// final response = await DioClient.uploadFile(
+  ///   '/api/upload',
+  ///   file: file,
+  ///   fileKey: 'image',
+  ///   fileName: 'avatar.jpg',
+  ///   fields: {'userId': '123'},
+  ///   fromJsonT: (json) => UploadResult.fromJson(json as Map<String, dynamic>),
+  /// );
+  /// ```
+  static Future<BaseCommonResponse<T>> uploadFile<T>(
+    String path, {
+    required File file,
+    required T Function(Object? json) fromJsonT,
+    String fileKey = 'file',
+    String? fileName,
+    Map<String, dynamic>? fields,
+    Map<String, dynamic>? queryParameters,
+    Options? options,
+    CancelToken? cancelToken,
+    ProgressCallback? onSendProgress,
+  }) async {
+    try {
+      // 检查文件是否存在
+      if (!await file.exists()) {
+        throw DioException(
+          requestOptions: RequestOptions(path: path),
+          error: '文件不存在: ${file.path}',
+          type: DioExceptionType.unknown,
+        );
+      }
+
+      // 构建文件名
+      final name = fileName ?? file.path.split('/').last;
+
+      // 创建 FormData
+      final formData = FormData.fromMap({
+        fileKey: await MultipartFile.fromFile(
+          file.path,
+          filename: name,
+        ),
+        if (fields != null) ...fields,
+      });
+
+      // 设置请求选项,使用 multipart/form-data
+      final requestOptions = options ?? Options();
+      requestOptions.headers ??= {};
+      requestOptions.headers!['Content-Type'] = 'multipart/form-data';
+
+      final response = await dio.post(
+        path,
+        data: formData,
+        queryParameters: queryParameters,
+        options: requestOptions,
+        cancelToken: cancelToken,
+        onSendProgress: onSendProgress,
+      );
+
+      return BaseCommonResponse<T>.fromJson(
+        response.data as Map<String, dynamic>,
+        fromJsonT,
+      );
+    } on DioException {
+      // 错误已由 ErrorInterceptor 处理,直接抛出
+      rethrow;
+    }
+  }
+
+  /// 多文件上传
+  /// 
+  /// [path] 请求路径(相对于 baseUrl)
+  /// [fromJsonT] JSON 转换为 T 类型的函数
+  /// [files] 要上传的文件列表
+  /// [fileKey] 文件字段名,默认为 'files'
+  /// [fields] 其他表单字段(可选)
+  /// [queryParameters] 查询参数
+  /// [options] 请求选项
+  /// [onSendProgress] 上传进度回调
+  /// 
+  /// 返回 [BaseCommonResponse] 对象
+  /// 
+  /// 示例:
+  /// ```dart
+  /// final files = [
+  ///   File('/path/to/image1.jpg'),
+  ///   File('/path/to/image2.jpg'),
+  /// ];
+  /// final response = await DioClient.uploadFiles(
+  ///   '/api/upload/multiple',
+  ///   files: files,
+  ///   fileKey: 'images',
+  ///   fields: {'albumId': '123'},
+  ///   fromJsonT: (json) => UploadResult.fromJson(json as Map<String, dynamic>),
+  /// );
+  /// ```
+  static Future<BaseCommonResponse<T>> uploadFiles<T>(
+    String path, {
+    required List<File> files,
+    required T Function(Object? json) fromJsonT,
+    String fileKey = 'files',
+    Map<String, dynamic>? fields,
+    Map<String, dynamic>? queryParameters,
+    Options? options,
+    CancelToken? cancelToken,
+    ProgressCallback? onSendProgress,
+  }) async {
+    try {
+      if (files.isEmpty) {
+        throw DioException(
+          requestOptions: RequestOptions(path: path),
+          error: '文件列表不能为空',
+          type: DioExceptionType.unknown,
+        );
+      }
+
+      // 检查所有文件是否存在
+      for (final file in files) {
+        if (!await file.exists()) {
+          throw DioException(
+            requestOptions: RequestOptions(path: path),
+            error: '文件不存在: ${file.path}',
+            type: DioExceptionType.unknown,
+          );
+        }
+      }
+
+      // 创建 MultipartFile 列表
+      final multipartFiles = await Future.wait(
+        files.map((file) async {
+          final fileName = file.path.split('/').last;
+          return MultipartFile.fromFile(
+            file.path,
+            filename: fileName,
+          );
+        }),
+      );
+
+      // 创建 FormData
+      final formData = FormData.fromMap({
+        fileKey: multipartFiles,
+        if (fields != null) ...fields,
+      });
+
+      // 设置请求选项,使用 multipart/form-data
+      final requestOptions = options ?? Options();
+      requestOptions.headers ??= {};
+      requestOptions.headers!['Content-Type'] = 'multipart/form-data';
+
+      final response = await dio.post(
+        path,
+        data: formData,
+        queryParameters: queryParameters,
+        options: requestOptions,
+        cancelToken: cancelToken,
+        onSendProgress: onSendProgress,
+      );
+
+      return BaseCommonResponse<T>.fromJson(
+        response.data as Map<String, dynamic>,
+        fromJsonT,
+      );
+    } on DioException {
+      // 错误已由 ErrorInterceptor 处理,直接抛出
+      rethrow;
+    }
+  }
+
+  /// 自定义 FormData 上传
+  /// 
+  /// 提供更灵活的文件上传方式,可以自定义 FormData 的内容
+  /// 
+  /// [path] 请求路径(相对于 baseUrl)
+  /// [fromJsonT] JSON 转换为 T 类型的函数
+  /// [formData] 自定义的 FormData 对象
+  /// [queryParameters] 查询参数
+  /// [options] 请求选项
+  /// [onSendProgress] 上传进度回调
+  /// 
+  /// 返回 [BaseCommonResponse] 对象
+  /// 
+  /// 示例:
+  /// ```dart
+  /// final formData = FormData.fromMap({
+  ///   'file1': await MultipartFile.fromFile('/path/to/file1.jpg'),
+  ///   'file2': await MultipartFile.fromFile('/path/to/file2.jpg'),
+  ///   'userId': '123',
+  ///   'description': '上传描述',
+  /// });
+  /// final response = await DioClient.uploadFormData(
+  ///   '/api/upload/custom',
+  ///   formData: formData,
+  ///   fromJsonT: (json) => UploadResult.fromJson(json as Map<String, dynamic>),
+  /// );
+  /// ```
+  static Future<BaseCommonResponse<T>> uploadFormData<T>(
+    String path, {
+    required FormData formData,
+    required T Function(Object? json) fromJsonT,
+    Map<String, dynamic>? queryParameters,
+    Options? options,
+    CancelToken? cancelToken,
+    ProgressCallback? onSendProgress,
+  }) async {
+    try {
+      // 设置请求选项,使用 multipart/form-data
+      final requestOptions = options ?? Options();
+      requestOptions.headers ??= {};
+      requestOptions.headers!['Content-Type'] = 'multipart/form-data';
+
+      final response = await dio.post(
+        path,
+        data: formData,
+        queryParameters: queryParameters,
+        options: requestOptions,
+        cancelToken: cancelToken,
+        onSendProgress: onSendProgress,
+      );
+
+      return BaseCommonResponse<T>.fromJson(
+        response.data as Map<String, dynamic>,
+        fromJsonT,
+      );
+    } on DioException {
       // 错误已由 ErrorInterceptor 处理,直接抛出
       rethrow;
     }