uploadPhoto static method

Future<PhotoUploadResponse> uploadPhoto(
  1. File file, {
  2. String photoType = 'general',
})

上传照片(支持照片类型)

file - 要上传的照片文件 photoType - 照片类型:'full_body', 'face_closeup', 'profile', 'tryon', 'general'

返回 PhotoUploadResponse 包含上传后的照片信息 抛出 UploadException 当上传失败时

Implementation

static Future<PhotoUploadResponse> uploadPhoto(
  File file, {
  String photoType = 'general',
}) async {
  print('📸 开始上传照片: ${file.path}');
  print('🔗 上传地址: $photoUploadUrl');
  print('📋 照片类型: $photoType');

  try {
    // 检查文件是否存在
    if (!await file.exists()) {
      throw UploadException('文件不存在', code: 'FILE_NOT_FOUND');
    }

    // 获取文件大小
    final fileSize = await file.length();
    print('📦 文件大小: ${(fileSize / 1024).toStringAsFixed(2)} KB');

    // 检查全身照和面部照的最小大小要求
    if ((photoType == 'full_body' || photoType == 'face_closeup') && fileSize < 50 * 1024) {
      throw UploadException(
        '照片质量不足,请选择更清晰的照片(至少50KB)',
        code: 'IMAGE_QUALITY_TOO_LOW',
      );
    }

    var request = http.MultipartRequest('POST', Uri.parse(photoUploadUrl));

    // 添加请求头
    request.headers.addAll({
      'User-Agent': 'FlightBookingApp/1.0.0',
      'Accept': 'application/json',
      'Connection': 'keep-alive',
    });

    // 获取文件扩展名并确定 MIME type
    final extension = file.path.toLowerCase().split('.').last;
    final mimeType = _getMimeType(extension);
    print('📄 文件扩展名: $extension, MIME类型: $mimeType');

    // 添加文件(显式指定 contentType)
    request.files.add(
      await http.MultipartFile.fromPath(
        'file',
        file.path,
        contentType: MediaType.parse(mimeType),
      ),
    );

    // 添加照片类型
    request.fields['type'] = photoType;

    // 发送请求(带超时)
    final streamedResponse = await request.send().timeout(
      Duration(seconds: EnvironmentConfig.apiTimeout),
      onTimeout: () {
        throw UploadException(
          '上传超时,请检查网络连接后重试',
          code: 'TIMEOUT',
        );
      },
    );

    final response = await http.Response.fromStream(streamedResponse);

    print('✅ 收到响应: ${response.statusCode}');

    // 处理成功响应
    if (response.statusCode == 200) {
      try {
        final jsonData = json.decode(response.body);
        if (jsonData['success'] == true) {
          print('✅ 照片上传成功: ${jsonData['url']}');
          if (jsonData['formatted_message'] != null) {
            print('📝 格式化消息: ${jsonData['formatted_message']}');
          }
          return PhotoUploadResponse.fromJson(jsonData);
        } else {
          final errorData = jsonData['error'] as Map<String, dynamic>?;
          final errorMsg = errorData?['message'] ?? '上传失败';
          final errorCode = errorData?['code'] ?? 'SERVER_ERROR';
          throw UploadException(errorMsg, code: errorCode);
        }
      } catch (e) {
        if (e is UploadException) rethrow;
        throw UploadException(
          '服务器响应格式错误',
          code: 'INVALID_RESPONSE',
          details: e.toString(),
        );
      }
    }

    // 处理错误响应
    if (response.statusCode == 400) {
      try {
        final jsonData = json.decode(response.body);
        final errorData = jsonData['error'] as Map<String, dynamic>?;
        final errorMsg = errorData?['message'] ?? '请求参数错误';
        final errorCode = errorData?['code'] ?? 'BAD_REQUEST';
        throw UploadException(errorMsg, code: errorCode, statusCode: 400);
      } catch (e) {
        if (e is UploadException) rethrow;
        throw UploadException('请求参数错误', code: 'BAD_REQUEST', statusCode: 400);
      }
    }

    // 处理其他错误状态码
    throw _handleErrorResponse(response);

  } on SocketException {
    throw UploadException(
      '网络连接失败,请检查网络设置',
      code: 'NETWORK_ERROR',
    );
  } on HttpException catch (e) {
    throw UploadException(
      '网络请求异常:${e.message}',
      code: 'HTTP_ERROR',
    );
  } on UploadException {
    rethrow;
  } catch (e) {
    print('❌ 上传异常: $e');
    throw UploadException(
      '上传失败:$e',
      code: 'UNKNOWN_ERROR',
      details: e.toString(),
    );
  }
}