Last active
November 12, 2024 04:26
-
-
Save orestesgaolin/69c112893532e1dd0e8fe01cb07ffc86 to your computer and use it in GitHub Desktop.
How to upload file to S3 with http library and progress updates (Flutter/Dart)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:async'; | |
import 'dart:convert'; | |
import 'dart:io'; | |
import 'package:meta/meta.dart'; | |
import 'package:mime/mime.dart'; | |
import 'package:path/path.dart'; | |
import 'package:http/http.dart' as http; | |
abstract class ApiClient { | |
Future<String> getPresignedUrl(File file, String space); | |
Future<bool> uploadFile( | |
File file, | |
String url, | |
ProgressUpdate onProgressUpdate, | |
); | |
} | |
class HttpApiClient implements ApiClient { | |
HttpApiClient({@required this.tokenProvider}) | |
: assert(tokenProvider != null) { | |
} | |
final TokenProvider tokenProvider; | |
static const presignUrl = ''; | |
@override | |
Future<bool> uploadFile( | |
File image, | |
String url, | |
ProgressUpdate onProgressUpdate, | |
) async { | |
try { | |
final mime = lookupMimeType(image.path); | |
final uri = Uri.parse(url); | |
var request = MultipartRequest( | |
'PUT', | |
uri, | |
onProgress: (int bytes, int total) { | |
onProgressUpdate?.call(bytes / total); | |
}, | |
); | |
request.headers.addAll({ | |
'Content-Type': mime, | |
}); | |
request.files.add( | |
await http.MultipartFile.fromPath(basename(image.path), image.path), | |
); | |
final response = await request.send(); | |
if (response.statusCode >= 200 && response.statusCode < 300) { | |
return true; | |
} else { | |
return false; | |
} | |
} on http.ClientException catch (e) { | |
logger.e('Error while sending file to API, ${e.message}'); | |
throw NetworkException(e.message); | |
} on SocketException catch (e) { | |
logger.e('Error while sending file to API', e); | |
throw NetworkException(e.message); | |
} catch (e) { | |
logger.e('Error while sending file to API', e); | |
return false; | |
} | |
} | |
@override | |
Future<String> getPresignedUrl( | |
File image, | |
String space, | |
) async { | |
try { | |
if (space == null || space.isEmpty) { | |
throw ArgumentError('Missing space'); | |
} | |
final token = await tokenProvider.token; | |
final name = basename(image.path); | |
final mime = lookupMimeType(image.path); | |
final uri = Uri.parse(presignUrl); | |
final request = http.Request('POST', uri); | |
request.body = jsonEncode({ | |
'contentType': mime, | |
'filename': '$name', | |
'space': '$space', | |
}); | |
request.headers.addAll({ | |
HttpHeaders.authorizationHeader: 'Bearer $token', | |
'content-type': 'application/json', | |
}); | |
final presignResult = await request.send(); | |
if (presignResult.statusCode == HttpStatus.accepted) { | |
final stream = await presignResult.stream.bytesToString(); | |
final data = jsonDecode(stream); | |
return data['location']; | |
} else { | |
logger.e( | |
'Error while generating presigned url: ${presignResult.reasonPhrase}', | |
); | |
return null; | |
} | |
} on ArgumentError { | |
rethrow; | |
} on SocketException catch (e) { | |
logger.e('Error while generating presigned url', e); | |
throw NetworkException(e.message); | |
} on Exception catch (e) { | |
logger.e('Error while generating presigned url', e); | |
return null; | |
} | |
} | |
} | |
class MultipartRequest extends http.MultipartRequest { | |
/// Creates a new [MultipartRequest]. | |
MultipartRequest( | |
String method, | |
Uri url, { | |
this.onProgress, | |
}) : super(method, url); | |
final void Function(int bytes, int totalBytes) onProgress; | |
/// Freezes all mutable fields and returns a | |
/// single-subscription [http.ByteStream] | |
/// that will emit the request body. | |
@override | |
http.ByteStream finalize() { | |
final byteStream = super.finalize(); | |
if (onProgress == null) return byteStream; | |
final total = contentLength; | |
var bytes = 0; | |
final t = StreamTransformer.fromHandlers( | |
handleData: (List<int> data, EventSink<List<int>> sink) { | |
bytes += data.length; | |
onProgress?.call(bytes, total); | |
sink.add(data); | |
}, | |
); | |
final stream = byteStream.transform(t); | |
return http.ByteStream(stream); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment