[REF] unify file upload logic and pool utils

- merge putMediaToCloud and putFileToPool into putFileToCloud
  with FileUploadMode for media-safe vs generic uploads

Signed-off-by: Texas0295 <kimura@texas0295.top>
This commit is contained in:
Texas0295
2025-09-21 22:38:49 +08:00
parent 1391fa0dde
commit ace302111a
11 changed files with 69 additions and 131 deletions

View File

@@ -66,7 +66,7 @@ class UpdateProfileScreen extends HookConsumerWidget {
final token = await getToken(ref.watch(tokenProvider));
if (token == null) throw ArgumentError('Token is null');
final cloudFile =
await putMediaToCloud(
await putFileToCloud(
fileData: UniversalFile(
data: result,
type: UniversalFileType.image,

View File

@@ -543,7 +543,7 @@ class EditChatScreen extends HookConsumerWidget {
final token = await getToken(ref.watch(tokenProvider));
if (token == null) throw ArgumentError('Token is null');
final cloudFile =
await putMediaToCloud(
await putFileToCloud(
fileData: UniversalFile(
data: result,
type: UniversalFileType.image,

View File

@@ -649,7 +649,7 @@ Future<void> loadMore() async {
var cloudAttachments = List.empty(growable: true);
for (var idx = 0; idx < attachments.length; idx++) {
final cloudFile =
await putMediaToCloud(
await putFileToCloud(
fileData: attachments[idx],
atk: token,
baseUrl: baseUrl,

View File

@@ -98,7 +98,7 @@ class EditPublisherScreen extends HookConsumerWidget {
final token = await getToken(ref.watch(tokenProvider));
if (token == null) throw ArgumentError('Token is null');
final cloudFile =
await putMediaToCloud(
await putFileToCloud(
fileData: UniversalFile(
data: result,
type: UniversalFileType.image,

View File

@@ -141,7 +141,7 @@ class EditAppScreen extends HookConsumerWidget {
final token = await getToken(ref.watch(tokenProvider));
if (token == null) throw ArgumentError('Token is null');
final cloudFile =
await putMediaToCloud(
await putFileToCloud(
fileData: UniversalFile(
data: result,
type: UniversalFileType.image,

View File

@@ -127,7 +127,7 @@ class EditBotScreen extends HookConsumerWidget {
final token = await getToken(ref.watch(tokenProvider));
if (token == null) throw ArgumentError('Token is null');
final cloudFile =
await putMediaToCloud(
await putFileToCloud(
fileData: UniversalFile(
data: result,
type: UniversalFileType.image,

View File

@@ -211,7 +211,7 @@ class EditRealmScreen extends HookConsumerWidget {
final token = await getToken(ref.watch(tokenProvider));
if (token == null) throw ArgumentError('Access token is null');
final cloudFile =
await putMediaToCloud(
await putFileToCloud(
fileData: UniversalFile(
data: result,
type: UniversalFileType.image,

View File

@@ -11,6 +11,8 @@ import 'package:island/services/file_uploader.dart';
import 'package:native_exif/native_exif.dart';
import 'package:path_provider/path_provider.dart';
enum FileUploadMode { generic, mediaSafe }
Future<XFile?> cropImage(
BuildContext context, {
required XFile image,
@@ -40,98 +42,46 @@ Future<XFile?> cropImage(
);
}
Completer<SnCloudFile?> putFileToPool({
required UniversalFile fileData,
required String atk,
required String baseUrl,
required String poolId,
String? filename,
String? mimetype,
Function(double progress, Duration estimate)? onProgress,
}) {
final completer = Completer<SnCloudFile?>();
final data = fileData.data;
if (data is! XFile) {
completer.completeError(
ArgumentError('Unsupported fileData type for putFileToPool'),
);
return completer;
}
final actualFilename = filename ?? data.name;
final actualMimetype = mimetype ?? data.mimeType ?? 'application/octet-stream';
final dio = Dio(BaseOptions(
baseUrl: baseUrl,
headers: {
'Authorization': 'AtField $atk',
'Accept': 'application/json',
},
));
final uploader = FileUploader(dio);
final fileObj = File(data.path);
onProgress?.call(0.0, Duration.zero);
uploader.uploadFile(
file: fileObj,
fileName: actualFilename,
contentType: actualMimetype,
poolId: poolId,
).then((result) {
onProgress?.call(1.0, Duration.zero);
completer.complete(result);
}).catchError((e) {
completer.completeError(e);
});
return completer;
}
Completer<SnCloudFile?> putMediaToCloud({
Completer<SnCloudFile?> putFileToCloud({
required UniversalFile fileData,
required String atk,
required String baseUrl,
String? poolId,
String? filename,
String? mimetype,
FileUploadMode? mode,
Function(double progress, Duration estimate)? onProgress,
}) {
final completer = Completer<SnCloudFile?>();
// Process the image to remove GPS EXIF data if needed
if (fileData.isOnDevice && fileData.type == UniversalFileType.image) {
final effectiveMode =
mode ??
(fileData.type == UniversalFileType.file
? FileUploadMode.generic
: FileUploadMode.mediaSafe);
if (effectiveMode == FileUploadMode.mediaSafe &&
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 = <String, String>{};
for (final attr in gpsAttributes) {
clearAttributes[attr] = '';
}
// Write empty values to remove GPS data
return exif.writeAttributes(clearAttributes);
.then((exif) async {
final gpsAttributes = {
'GPSLatitude': '',
'GPSLatitudeRef': '',
'GPSLongitude': '',
'GPSLongitudeRef': '',
'GPSAltitude': '',
'GPSAltitudeRef': '',
'GPSTimeStamp': '',
'GPSProcessingMethod': '',
'GPSDateStamp': '',
};
await exif.writeAttributes(gpsAttributes);
})
.then((_) {
// Continue with upload after GPS data is removed
_processUpload(
.then(
(_) => _processUpload(
fileData,
atk,
baseUrl,
@@ -140,12 +90,11 @@ Completer<SnCloudFile?> putMediaToCloud({
mimetype,
onProgress,
completer,
);
})
),
)
.catchError((e) {
// If there's an error, continue with the original file
debugPrint('Error removing GPS EXIF data: $e');
_processUpload(
return _processUpload(
fileData,
atk,
baseUrl,
@@ -161,7 +110,6 @@ Completer<SnCloudFile?> putMediaToCloud({
}
}
// If not an image or on web, continue with normal upload
_processUpload(
fileData,
atk,

View File

@@ -55,7 +55,7 @@ class CloudFilePicker extends HookConsumerWidget {
uploadPosition.value = idx;
final file = files.value[idx];
final cloudFile =
await putMediaToCloud(
await putFileToCloud(
fileData: file,
atk: token,
baseUrl: baseUrl,

View File

@@ -185,7 +185,7 @@ class ComposeLogic {
if (attachment.data is! SnCloudFile) {
try {
final cloudFile =
await putMediaToCloud(
await putFileToCloud(
fileData: attachment,
atk: token,
baseUrl: baseUrl,
@@ -523,44 +523,34 @@ class ComposeLogic {
SnCloudFile? cloudFile;
final pools = await ref.read(poolsProvider.future);
final selectedPoolId = resolveDefaultPoolId(ref, pools);
if (attachment.type == UniversalFileType.file) {
cloudFile =
await putFileToPool(
fileData: attachment,
atk: token,
baseUrl: baseUrl,
poolId: selectedPoolId,
filename: attachment.data.name ?? 'General file',
mimetype:
attachment.data.mimeType ??
getMimeTypeFromFileType(attachment.type),
onProgress: (progress, _) {
state.attachmentProgress.value = {
...state.attachmentProgress.value,
index: progress,
};
},
).future;
} else {
cloudFile =
await putMediaToCloud(
fileData: attachment,
atk: token,
baseUrl: baseUrl,
filename: attachment.data.name ?? 'Post media',
mimetype:
attachment.data.mimeType ??
getMimeTypeFromFileType(attachment.type),
onProgress: (progress, _) {
state.attachmentProgress.value = {
...state.attachmentProgress.value,
index: progress,
};
},
).future;
}
final pools = await ref.read(poolsProvider.future);
final selectedPoolId = resolveDefaultPoolId(ref, pools);
cloudFile =
await putFileToCloud(
fileData: attachment,
atk: token,
baseUrl: baseUrl,
poolId: selectedPoolId,
filename:
attachment.data.name ??
(attachment.type == UniversalFileType.file
? 'General file'
: 'Post media'),
mimetype:
attachment.data.mimeType ??
getMimeTypeFromFileType(attachment.type),
mode:
attachment.type == UniversalFileType.file
? FileUploadMode.generic
: FileUploadMode.mediaSafe,
onProgress: (progress, _) {
state.attachmentProgress.value = {
...state.attachmentProgress.value,
index: progress,
};
},
).future;
if (cloudFile == null) {
throw ArgumentError('Failed to upload the file...');

View File

@@ -247,7 +247,7 @@ class _ShareSheetState extends ConsumerState<ShareSheet> {
for (var idx = 0; idx < universalFiles.length; idx++) {
final file = universalFiles[idx];
final cloudFile =
await putMediaToCloud(
await putFileToCloud(
fileData: file,
atk: token,
baseUrl: serverUrl,