download static method
Downloads uri to savePath and reports progress as a stream.
Each event contains the received bytes, the total size (when the server
sends content-length), the remaining bytes and the percent. The last
event has isDone == true. Errors (including non-2xx responses) are
emitted as FinchHttpException; a partial file is deleted on failure.
Implementation
static Stream<DownloadProgress> download(
Uri uri,
String savePath, {
Map<String, String>? headers,
}) async* {
final client = HttpClient();
final file = File(savePath);
IOSink? sink;
var received = 0;
var finished = false;
try {
final request = await client.getUrl(uri);
headers?.forEach(request.headers.set);
final response = await request.close();
if (response.statusCode < 200 || response.statusCode >= 300) {
await response.drain<void>();
throw FinchHttpException(
'Failed to download: ${response.reasonPhrase}',
statusCode: response.statusCode,
);
}
final total = response.contentLength >= 0 ? response.contentLength : null;
await file.parent.create(recursive: true);
sink = file.openWrite();
yield DownloadProgress(received: 0, total: total);
await for (final chunk in response) {
sink.add(chunk);
received += chunk.length;
yield DownloadProgress(received: received, total: total);
}
await sink.flush();
await sink.close();
sink = null;
finished = true;
yield DownloadProgress(
received: received,
total: total,
isDone: true,
);
} on FinchHttpException {
rethrow;
} catch (e) {
throw FinchHttpException('Failed to download: $e');
} finally {
client.close(force: true);
try {
await sink?.close();
} catch (_) {}
if (!finished && await file.exists()) {
await file.delete();
}
}
}