I have the following code to upload a file.
var client = new dio.Dio();
await client.put(
url,
data: formData,
options: dio.Options(
headers: headers,
),
onSendProgress: (int sent, int total) {
final progress = sent / total;
print('progress: $progress ($sent/$total)');
},
);
Uploading the file works fine. The thing is, I have a view that has a circular progress bar that indicates the upload progress when onSendProgress
is triggered. When I upload a ~10MB file the progress bars jumps from 0% to 100% within a second and then waits for a couple of seconds (depending on the file size) before continuing with the rest of the code. This seems weird to me because I expect the progress bar to gradually increase its progress.
Below you can find the print outputs from my example with a ~10MB file.
flutter: progress: 0.000003035281907563734 (29/9554302)
flutter: progress: 0.000013187776563897604 (126/9554302)
flutter: progress: 0.999996546058519 (9554269/9554302)
flutter: progress: 0.9999967553883057 (9554271/9554302)
flutter: progress: 1.0 (9554302/9554302)
This one is with a smaller file, but the same amount of callbacks.
flutter: progress: 0.00001847214941293598 (29/1569931)
flutter: progress: 0.00008025830434585978 (126/1569931)
flutter: progress: 0.9999789799679094 (1569898/1569931)
flutter: progress: 0.9999802539092483 (1569900/1569931)
flutter: progress: 1.0 (1569931/1569931)
As you can see it jumps from 0% to 100%. I can imagine you thinking, that is just fast internet. But I've tried Wifi, 4G, 3G and they all show the same issue. I hit the upload button, it start on 0% it jumps to 100% and then I have to wait some time (depending on the file size) for the upload to finish.
Is there a way to get more amount of onSendProgress
callbacks triggered during the upload or somehow to delay the upload so I can get a smooth upload progress?
EDIT:
I've tried the following with the HTTP package
import 'dart:async';
import 'package:http/http.dart' as http;
class UploadRequest extends http.MultipartRequest {
final void Function(int bytes, int totalBytes) onProgress;
UploadRequest(
String method,
Uri url, {
this.onProgress,
}) : super(method, url);
http.ByteStream finalize() {
final byteStream = super.finalize();
if (onProgress == null) return byteStream;
final total = this.contentLength;
final t = StreamTransformer.fromHandlers(
handleData: (List<int> data, EventSink<List<int>> sink) {
onProgress(data.length, total);
sink.add(data);
},
);
final stream = byteStream.transform(t);
return http.ByteStream(stream);
}
}
Trigger:
final request = UploadRequest(
'PUT',
Uri.parse(url),
onProgress: (int bytes, int total) {
print('progress: $progress ($sent/$total)');
},
);
request.headers.addAll(headers);
request.files.add(
http.MultipartFile.fromBytes(
'file',
attachment.file.buffer.asUint8List(),
filename: attachment.name,
contentType: MediaType.parse(attachment.contentType),
),
);
var response = await request.send();
But sadly, this has the same issue. The file uploads fine, but the progress callback is called only a few times. It hits 100% right away, then I have to wait a while (depending on the file size) before getting a 2xx response from the server. So I don't think this is a specific DIO issue.
I think that it looks like it is not actually showing the upload progress, but rather the progress of the file being read into a stream clientside. Then the stream is uploaded to the server and that is what you are waiting on (of course depending on the file size) even though the progress shows 100%.
To set the progress, find the Progress Value input box and enter the value between 0 to 1.0. For example, the value of 0.3 will fill 30% of the portion on the ProgressBar. To change the progress text (displayed in the center), scroll down to the Text section and find the Text property and enter the value.
This appears to be a bug on dio
, I can see two ways you can get around this:
Just add a number to your circular progress, it will say 99%
(truncate the rest) so the users will know its not done yet, as 100%
will only happen when you get 1.0
as a result).
use the http package instead. the http
package as a StreamedRequest
which would give you finer control over what's being sent, so you can reflect this on your UI.
I have found the resolution.
Just use file.openRead()
as the request's data
param while PUT
a file.
For MultipartFile
maybe it can use MultipartFile(file.openRead(), file.lenthSync())
. But I did not verify this, someone can try it if necessary.
var client = Dio();
await client.put(
url,
data: file.openRead(),
options: dio.Options(
headers: headers,
),
onSendProgress: (int sent, int total) {
print('progress: ${(sent / total * 100).toStringAsFixed(0)}% ($sent/$total)');
},
);
The progress callback is smooth now.
I/flutter (19907): progress: 9% (65536/734883)
I/flutter (19907): progress: 18% (131072/734883)
I/flutter (19907): progress: 27% (196608/734883)
I/flutter (19907): progress: 36% (262144/734883)
I/flutter (19907): progress: 45% (327680/734883)
I/flutter (19907): progress: 54% (393216/734883)
I/flutter (19907): progress: 62% (458752/734883)
I/flutter (19907): progress: 71% (524288/734883)
I/flutter (19907): progress: 80% (589824/734883)
I/flutter (19907): progress: 89% (655360/734883)
I/flutter (19907): progress: 98% (720896/734883)
I/flutter (19907): progress: 100% (734883/734883)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With