uploadPhoto static method
上传照片(支持照片类型)
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(),
);
}
}