FunyMeeAI/lib/features/report/report_feedback_upload.dart

61 lines
1.9 KiB
Dart

import 'dart:io';
import 'package:client_proxy_framework/client_proxy_framework.dart';
import 'package:http/http.dart' as http;
/// PUT 到 [FeedbackApi.getUploadPresignedUrl] 返回的地址,返回服务端 [FeedbackUploadPresignedUrlResponse.filePath] 供 [FeedbackApi.submit] 的 `fileUrls`。
Future<String> uploadFeedbackAttachment(File file) async {
final name = file.path.split('/').last;
final presignedRes = await FeedbackApi.getUploadPresignedUrl(fileName: name);
if (!presignedRes.isSuccess || presignedRes.data == null) {
throw StateError(
presignedRes.msg.isNotEmpty
? presignedRes.msg
: 'Could not get upload URL',
);
}
final p = presignedRes.data!;
await ImageUploadExpectedSizeCache.writeFeedbackExpectedSize(p.expectedSize);
final uploadUrl = p.uploadUrl;
final filePath = p.filePath;
if (uploadUrl == null ||
uploadUrl.isEmpty ||
filePath == null ||
filePath.isEmpty) {
throw StateError('Invalid upload URL response');
}
final bytes = await file.readAsBytes();
final contentType = _mimeForPath(file.path);
final headers = <String, String>{'Content-Type': contentType};
final extra = p.putHeaders;
if (extra != null) {
for (final e in extra.entries) {
final k = e.key.trim();
if (k.isEmpty) continue;
headers[k] = e.value.toString();
}
}
if (!headers.containsKey('Content-Type')) {
headers['Content-Type'] = contentType;
}
final r = await http.put(
Uri.parse(uploadUrl),
headers: headers,
body: bytes,
);
if (r.statusCode < 200 || r.statusCode >= 300) {
throw StateError('Upload failed (${r.statusCode})');
}
return filePath;
}
String _mimeForPath(String path) {
final lower = path.toLowerCase();
if (lower.endsWith('.png')) return 'image/png';
if (lower.endsWith('.gif')) return 'image/gif';
if (lower.endsWith('.webp')) return 'image/webp';
return 'image/jpeg';
}