|
|
|
import 'dart:io';
|
|
|
|
|
|
|
|
import 'package:dio/dio.dart';
|
|
|
|
import 'package:flutter/foundation.dart'; // 引入 kDebugMode 和 debugPrint
|
|
|
|
import 'package:get/get.dart' hide FormData, MultipartFile;
|
|
|
|
import 'package:path/path.dart' as p;
|
|
|
|
import 'package:path_provider/path_provider.dart';
|
|
|
|
import 'package:problem_check_system/core/extensions/http_response_extension.dart';
|
|
|
|
import 'package:problem_check_system/core/utils/constants/api_endpoints.dart';
|
|
|
|
import 'package:problem_check_system/data/models/image_metadata_model.dart';
|
|
|
|
import 'package:problem_check_system/data/models/image_status.dart';
|
|
|
|
import 'package:problem_check_system/data/providers/http_provider.dart';
|
|
|
|
|
|
|
|
class FileRepository {
|
|
|
|
final HttpProvider _httpProvider = Get.find<HttpProvider>();
|
|
|
|
|
|
|
|
/// @param imageFilePath 要上传的本地图片文件。
|
|
|
|
/// @param cancelToken 用于取消上传任务的令牌。
|
|
|
|
/// @param onSendProgress 上传进度回调,提供已发送和总大小。
|
|
|
|
/// @return 上传成功后服务器返回的图片 URL。
|
|
|
|
Future<String> uploadImage(
|
|
|
|
String imageFilePath, {
|
|
|
|
required CancelToken cancelToken,
|
|
|
|
ProgressCallback? onSendProgress,
|
|
|
|
}) async {
|
|
|
|
try {
|
|
|
|
// 1. 创建 FormData 对象,用于构建 multipart/form-data 请求体
|
|
|
|
final formData = FormData.fromMap({
|
|
|
|
// 'file': 这通常是后端接口定义的文件字段名
|
|
|
|
'file': await MultipartFile.fromFile(
|
|
|
|
imageFilePath,
|
|
|
|
filename: p.basename(imageFilePath),
|
|
|
|
),
|
|
|
|
});
|
|
|
|
|
|
|
|
// 2. 使用 HttpProvider 的 post 方法发送请求
|
|
|
|
final response = await _httpProvider.post(
|
|
|
|
ApiEndpoints.postUploadFile,
|
|
|
|
data: formData,
|
|
|
|
cancelToken: cancelToken, // 将取消令牌传递给 post 请求
|
|
|
|
onSendProgress: onSendProgress, // 将进度回调传递给 post 请求
|
|
|
|
);
|
|
|
|
|
|
|
|
// --- 在这里打印服务器的完整响应结构 (仅在调试模式下) ---
|
|
|
|
if (kDebugMode) {
|
|
|
|
debugPrint('服务器返回的状态码: ${response.statusCode}');
|
|
|
|
debugPrint('服务器返回的原始数据: ${response.data}');
|
|
|
|
}
|
|
|
|
|
|
|
|
// 3. 处理响应,并返回图片 URL
|
|
|
|
if (response.isSuccess) {
|
|
|
|
final Map<String, dynamic> data = response.data;
|
|
|
|
|
|
|
|
// 假设服务器返回的图片 URL 字段名为 'url'
|
|
|
|
String imageUrl = data['fileName'];
|
|
|
|
|
|
|
|
return imageUrl;
|
|
|
|
} else {
|
|
|
|
throw Exception('上传失败,状态码: ${response.statusCode}');
|
|
|
|
}
|
|
|
|
} on DioException catch (e) {
|
|
|
|
Get.log('图片上传发生未知错误: $e');
|
|
|
|
throw Exception('图片上传失败: ${e.message}');
|
|
|
|
} catch (e) {
|
|
|
|
Get.log('图片上传发生未知错误: $e');
|
|
|
|
throw Exception('图片上传发生未知错误: $e');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 新增的下载方法
|
|
|
|
Future<ImageMetadata> downloadImage(
|
|
|
|
String imageUrl, {
|
|
|
|
CancelToken? cancelToken,
|
|
|
|
void Function(int received, int total)? onReceiveProgress,
|
|
|
|
}) async {
|
|
|
|
final directory = await getApplicationDocumentsDirectory();
|
|
|
|
final imagesDir = Directory('${directory.path}/problem_images');
|
|
|
|
|
|
|
|
// 确保目录存在
|
|
|
|
if (!await imagesDir.exists()) {
|
|
|
|
await imagesDir.create(recursive: true);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 生成唯一的文件名
|
|
|
|
final String fileName =
|
|
|
|
'downloaded_${DateTime.now().millisecondsSinceEpoch}_${imageUrl.hashCode}${_getFileExtension(imageUrl)}';
|
|
|
|
final String imagePath = '${imagesDir.path}/$fileName';
|
|
|
|
|
|
|
|
try {
|
|
|
|
// 下载图片
|
|
|
|
await _httpProvider.download(
|
|
|
|
imageUrl,
|
|
|
|
imagePath,
|
|
|
|
cancelToken: cancelToken,
|
|
|
|
onReceiveProgress: onReceiveProgress,
|
|
|
|
);
|
|
|
|
|
|
|
|
// 返回图片元数据
|
|
|
|
return ImageMetadata(
|
|
|
|
localPath: imagePath,
|
|
|
|
remoteUrl: imageUrl,
|
|
|
|
status: ImageStatus.synced,
|
|
|
|
);
|
|
|
|
} catch (e) {
|
|
|
|
// 清理可能创建的不完整文件
|
|
|
|
final file = File(imagePath);
|
|
|
|
if (await file.exists()) {
|
|
|
|
await file.delete();
|
|
|
|
}
|
|
|
|
rethrow;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 批量下载方法
|
|
|
|
Future<List<ImageMetadata>> downloadImages(
|
|
|
|
List<String> imageUrls, {
|
|
|
|
CancelToken? cancelToken,
|
|
|
|
void Function(int current, int total)? onProgress,
|
|
|
|
}) async {
|
|
|
|
final List<ImageMetadata> results = [];
|
|
|
|
int downloadedCount = 0;
|
|
|
|
|
|
|
|
for (final imageUrl in imageUrls) {
|
|
|
|
if (cancelToken?.isCancelled == true) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
final metadata = await downloadImage(
|
|
|
|
imageUrl,
|
|
|
|
cancelToken: cancelToken,
|
|
|
|
onReceiveProgress: (received, total) {
|
|
|
|
// 单个文件的进度可以在这里处理
|
|
|
|
},
|
|
|
|
);
|
|
|
|
results.add(metadata);
|
|
|
|
|
|
|
|
// 更新总体进度
|
|
|
|
downloadedCount++;
|
|
|
|
onProgress?.call(downloadedCount, imageUrls.length);
|
|
|
|
} catch (e) {
|
|
|
|
Get.log('Failed to download image $imageUrl: $e');
|
|
|
|
// 可以选择继续下载其他图片或抛出异常
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return results;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 辅助方法:获取文件扩展名
|
|
|
|
String _getFileExtension(String url) {
|
|
|
|
try {
|
|
|
|
final uri = Uri.parse(url);
|
|
|
|
final pathSegments = uri.pathSegments;
|
|
|
|
if (pathSegments.isNotEmpty) {
|
|
|
|
final fileName = pathSegments.last;
|
|
|
|
final dotIndex = fileName.lastIndexOf('.');
|
|
|
|
if (dotIndex != -1 && dotIndex < fileName.length - 1) {
|
|
|
|
return fileName.substring(dotIndex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return '.jpg';
|
|
|
|
} catch (e) {
|
|
|
|
return '.jpg';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|