From 15a5848785262e194e988400c3e689bc452bf50a Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Wed, 18 Jun 2025 23:07:51 +0800 Subject: [PATCH] :sparkles: Client side remove GPS EXIF --- lib/services/file.dart | 104 ++++++++++++++++++++++++++++++++++++++--- pubspec.lock | 8 ++++ pubspec.yaml | 1 + 3 files changed, 106 insertions(+), 7 deletions(-) diff --git a/lib/services/file.dart b/lib/services/file.dart index de8f831..93be01c 100644 --- a/lib/services/file.dart +++ b/lib/services/file.dart @@ -8,6 +8,7 @@ import 'package:cross_file/cross_file.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:island/models/file.dart'; +import 'package:native_exif/native_exif.dart'; import 'package:tus_client_dart/tus_client_dart.dart'; Future cropImage( @@ -46,7 +47,91 @@ Completer putMediaToCloud({ String? mimetype, Function(double progress, Duration estimate)? onProgress, }) { - XFile file; + final completer = Completer(); + + // Process the image to remove GPS EXIF data if needed + if (fileData.isOnDevice && fileData.type == UniversalFileType.image) { + final data = fileData.data; + if (data is XFile && !kIsWeb && (Platform.isIOS || Platform.isAndroid)) { + // Use native_exif to selectively remove GPS data + Exif.fromPath(data.path) + .then((exif) { + // Remove GPS-related attributes + final gpsAttributes = [ + 'GPSLatitude', + 'GPSLatitudeRef', + 'GPSLongitude', + 'GPSLongitudeRef', + 'GPSAltitude', + 'GPSAltitudeRef', + 'GPSTimeStamp', + 'GPSProcessingMethod', + 'GPSDateStamp', + ]; + + // Create a map of attributes to clear + final clearAttributes = {}; + for (final attr in gpsAttributes) { + clearAttributes[attr] = ''; + } + + // Write empty values to remove GPS data + return exif.writeAttributes(clearAttributes); + }) + .then((_) { + // Continue with upload after GPS data is removed + _processUpload( + fileData, + atk, + baseUrl, + filename, + mimetype, + onProgress, + completer, + ); + }) + .catchError((e) { + // If there's an error, continue with the original file + debugPrint('Error removing GPS EXIF data: $e'); + _processUpload( + fileData, + atk, + baseUrl, + filename, + mimetype, + onProgress, + completer, + ); + }); + + return completer; + } + } + + // If not an image or on web, continue with normal upload + _processUpload( + fileData, + atk, + baseUrl, + filename, + mimetype, + onProgress, + completer, + ); + return completer; +} + +// Helper method to process the upload after any EXIF processing +Completer _processUpload( + UniversalFile fileData, + String atk, + String baseUrl, + String? filename, + String? mimetype, + Function(double progress, Duration estimate)? onProgress, + Completer completer, +) { + late XFile file; String actualFilename = filename ?? 'randomly_file'; String actualMimetype = mimetype ?? ''; Uint8List? byteData; @@ -63,16 +148,23 @@ Completer putMediaToCloud({ actualFilename = filename ?? 'uploaded_file'; actualMimetype = mimetype ?? 'application/octet-stream'; if (mimetype == null) { - throw ArgumentError('Mimetype is required when providing raw bytes.'); + completer.completeError( + ArgumentError('Mimetype is required when providing raw bytes.'), + ); + return completer; } file = XFile.fromData(byteData!, mimeType: actualMimetype); } else if (data is SnCloudFile) { // If the file is already on the cloud, just return it - return Completer()..complete(data); + completer.complete(data); + return completer; } else { - throw ArgumentError( - 'Invalid fileData type. Expected data to be XFile, List, Uint8List, or SnCloudFile.', + completer.completeError( + ArgumentError( + 'Invalid fileData type. Expected data to be XFile, List, Uint8List, or SnCloudFile.', + ), ); + return completer; } final Map metadata = { @@ -80,8 +172,6 @@ Completer putMediaToCloud({ 'content-type': actualMimetype, }; - final completer = Completer(); - final client = TusClient(file); client .upload( diff --git a/pubspec.lock b/pubspec.lock index 93b7416..8fcaca5 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1397,6 +1397,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" + native_exif: + dependency: "direct main" + description: + name: native_exif + sha256: "0d37444c1ed00cbcada69b7510aba1d505fed75d3b6ef3ea3c8c2c970040e4f1" + url: "https://pub.dev" + source: hosted + version: "0.6.2" nested: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index ccf7028..8c8a3a4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -115,6 +115,7 @@ dependencies: fl_chart: ^1.0.0 sign_in_with_apple: ^7.0.1 flutter_svg: ^2.1.0 + native_exif: ^0.6.2 dev_dependencies: flutter_test: