Update: 2026-06-25 03:28:59
This commit is contained in:
@@ -282,13 +282,21 @@ class RegistrationController extends GetxController {
|
||||
}
|
||||
|
||||
/// التصرّف العام لاختيار/قص/ضغط/رفع الصورة حسب type
|
||||
Future<void> choosImage(String link, String imageType) async {
|
||||
Future<void> choosImage(String link, String imageType, {String? backupLink}) async {
|
||||
if (isloading) return;
|
||||
try {
|
||||
isloading = true;
|
||||
update();
|
||||
|
||||
final pickedImage = await picker.pickImage(
|
||||
source: ImageSource.camera,
|
||||
preferredCameraDevice: CameraDevice.rear,
|
||||
);
|
||||
if (pickedImage == null) return;
|
||||
if (pickedImage == null) {
|
||||
isloading = false;
|
||||
update();
|
||||
return;
|
||||
}
|
||||
|
||||
image = File(pickedImage.path);
|
||||
|
||||
@@ -305,14 +313,15 @@ class RegistrationController extends GetxController {
|
||||
IOSUiSettings(title: 'Cropper'.tr),
|
||||
],
|
||||
);
|
||||
if (croppedFile == null) return;
|
||||
if (croppedFile == null) {
|
||||
isloading = false;
|
||||
update();
|
||||
return;
|
||||
}
|
||||
|
||||
// صورة للمعاينة داخل التطبيق
|
||||
myImage = File(croppedFile.path);
|
||||
|
||||
isloading = true;
|
||||
update();
|
||||
|
||||
// ضغط (وأيضاً يمكنك إضافة rotateImageIfNeeded قبل/بعد الضغط إن رغبت)
|
||||
final File compressedImage = await compressImage(File(croppedFile.path));
|
||||
|
||||
@@ -320,12 +329,14 @@ class RegistrationController extends GetxController {
|
||||
final driverId = box.read(BoxName.driverID);
|
||||
|
||||
final payload = <String, String>{
|
||||
'driverID': driverId,
|
||||
'imageType': imageType, // مثال: driver_license_front
|
||||
'driverID': driverId?.toString() ?? '',
|
||||
'imageType': imageType,
|
||||
'country': box.read(BoxName.countryCode) ?? '',
|
||||
};
|
||||
|
||||
// الرفع وإرجاع الرابط
|
||||
final String imageUrl = await uploadImage(compressedImage, payload, link);
|
||||
// الرفع وإرجاع الرابط (يحاول الأساسي ثم الاحتياطي)
|
||||
final String imageUrl =
|
||||
await uploadImage(compressedImage, payload, link, backupLink: backupLink);
|
||||
|
||||
// حفظ الرابط محلياً حسب النوع
|
||||
docUrls[imageType] = imageUrl;
|
||||
@@ -340,68 +351,79 @@ class RegistrationController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
/// ترفع الملف وترجع رابط الصورة النهائي كـ String مع إعادة المحاولة في حال فشل الاتصال
|
||||
/// ترفع الملف وترجع رابط الصورة النهائي كـ String مع إعادة المحاولة.
|
||||
/// يحاول الرابط الأساسي first، فإن فشل جرب الاحتياطي (إن وُجد).
|
||||
Future<String> uploadImage(
|
||||
File file, Map<String, String> data, String link) async {
|
||||
int maxRetries = 3;
|
||||
int attempt = 0;
|
||||
while (attempt < maxRetries) {
|
||||
attempt++;
|
||||
try {
|
||||
final uri = Uri.parse(link);
|
||||
final request = http.MultipartRequest('POST', uri);
|
||||
File file,
|
||||
Map<String, String> data,
|
||||
String link, {
|
||||
String? backupLink,
|
||||
}) async {
|
||||
const int maxRetries = 3;
|
||||
|
||||
final _jwt = box.read(BoxName.jwt);
|
||||
final String _token = _jwt != null ? r(_jwt).split(AppInformation.addd)[0] : '';
|
||||
final headers = <String, String>{
|
||||
'Authorization': 'Bearer $_token',
|
||||
'X-HMAC-Auth': '${box.read(BoxName.hmac)}',
|
||||
};
|
||||
request.headers.addAll(headers);
|
||||
final List<String> urlsToTry = [link, if (backupLink != null && backupLink.trim().isNotEmpty) backupLink];
|
||||
|
||||
final forcedName = '${box.read(BoxName.driverID) ?? 'image'}.jpg';
|
||||
for (final currentUrl in urlsToTry) {
|
||||
for (int attempt = 1; attempt <= maxRetries; attempt++) {
|
||||
try {
|
||||
final uri = Uri.parse(currentUrl);
|
||||
final request = http.MultipartRequest('POST', uri);
|
||||
|
||||
request.files.add(
|
||||
await http.MultipartFile.fromPath(
|
||||
'image',
|
||||
file.path,
|
||||
filename: forcedName,
|
||||
),
|
||||
);
|
||||
final _jwt = box.read(BoxName.jwt);
|
||||
final String _token = _jwt != null ? r(_jwt).split(AppInformation.addd)[0] : '';
|
||||
final headers = <String, String>{
|
||||
'Authorization': 'Bearer $_token',
|
||||
'X-HMAC-Auth': '${box.read(BoxName.hmac)}',
|
||||
};
|
||||
request.headers.addAll(headers);
|
||||
|
||||
data.forEach((k, v) => request.fields[k] = v);
|
||||
final forcedName = '${box.read(BoxName.driverID) ?? 'image'}.jpg';
|
||||
|
||||
// المهلة الزمنية 120 ثانية لتناسب الاتصالات الضعيفة
|
||||
final streamed =
|
||||
await request.send().timeout(const Duration(seconds: 120));
|
||||
final res = await http.Response.fromStream(streamed);
|
||||
request.files.add(
|
||||
await http.MultipartFile.fromPath(
|
||||
'image',
|
||||
file.path,
|
||||
filename: forcedName,
|
||||
),
|
||||
);
|
||||
|
||||
if (res.statusCode != 200) {
|
||||
throw Exception(
|
||||
'Failed to upload image: ${res.statusCode} - ${res.body}');
|
||||
data.forEach((k, v) => request.fields[k] = v);
|
||||
|
||||
final streamed =
|
||||
await request.send().timeout(const Duration(seconds: 120));
|
||||
final res = await http.Response.fromStream(streamed);
|
||||
|
||||
if (res.statusCode != 200) {
|
||||
throw Exception(
|
||||
'Failed to upload image: ${res.statusCode} - ${res.body}');
|
||||
}
|
||||
|
||||
final body = jsonDecode(res.body);
|
||||
final String? url = body['url'] ??
|
||||
body['file_link'] ??
|
||||
body['image_url'] ??
|
||||
(body['data'] is Map ? body['data']['url'] : null);
|
||||
|
||||
if (url == null || url.isEmpty) {
|
||||
throw Exception(
|
||||
'Upload succeeded but no image URL found in response: ${res.body}');
|
||||
}
|
||||
|
||||
return url;
|
||||
} catch (e) {
|
||||
Log.print("⚠️ [Upload Attempt $attempt/$maxRetries @ $currentUrl] Error: $e");
|
||||
if (attempt >= maxRetries) {
|
||||
if (urlsToTry.last == currentUrl) {
|
||||
rethrow;
|
||||
}
|
||||
Log.print("➡️ Switching to backup URL...");
|
||||
} else {
|
||||
await Future.delayed(Duration(seconds: attempt * 2));
|
||||
}
|
||||
}
|
||||
|
||||
final body = jsonDecode(res.body);
|
||||
final String? url = body['url'] ??
|
||||
body['file_link'] ??
|
||||
body['image_url'] ??
|
||||
(body['data'] is Map ? body['data']['url'] : null);
|
||||
|
||||
if (url == null || url.isEmpty) {
|
||||
throw Exception(
|
||||
'Upload succeeded but no image URL found in response: ${res.body}');
|
||||
}
|
||||
|
||||
return url;
|
||||
} catch (e) {
|
||||
Log.print("⚠️ [Image Upload Attempt $attempt Failed] Error: $e");
|
||||
if (attempt >= maxRetries) {
|
||||
rethrow;
|
||||
}
|
||||
await Future.delayed(Duration(seconds: attempt * 2));
|
||||
}
|
||||
}
|
||||
throw Exception('Upload failed after $maxRetries attempts');
|
||||
throw Exception('Upload failed after all attempts');
|
||||
}
|
||||
|
||||
Future<File> compressImage(File file) async {
|
||||
|
||||
Reference in New Issue
Block a user