Flutter: как увеличить количество вызовов onSendProgress для плавной анимации индикатора выполнения при загрузке файлов?
У меня есть следующий код для загрузки файла.
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)');
},
);
Загрузка файла работает нормально. Дело в том, что у меня есть представление с круговой шкалой выполнения, которая показывает прогресс загрузки, когдаonSendProgress
срабатывает. Когда я загружаю файл размером ~10 МБ, индикатор выполнения перескакивает с 0% на 100% в течение секунды, а затем ждет пару секунд (в зависимости от размера файла), прежде чем продолжить выполнение остальной части кода. Мне это кажется странным, потому что я ожидаю, что индикатор выполнения будет постепенно увеличивать свой прогресс.
Ниже вы можете найти результаты печати из моего примера с файлом размером ~10 МБ.
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)
Это с меньшим файлом, но с таким же количеством обратных вызовов.
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)
Как вы можете видеть, значение изменяется от 0% до 100%. Я могу представить, как вы думаете, что это просто быстрый интернет. Но я пробовал Wi-Fi, 4G, 3G, и все они показывают одну и ту же проблему. Я нажимаю кнопку загрузки, она начинается с 0%, она переходит на 100%, а затем мне нужно подождать некоторое время (в зависимости от размера файла), пока загрузка не завершится.
Есть ли способ получить больше onSendProgress
обратные вызовы срабатывают во время загрузки или как-то задерживают загрузку, чтобы я мог добиться плавного процесса загрузки?
РЕДАКТИРОВАТЬ:
Я пробовал следующее с пакетом HTTP
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);
}
}
Вызывать:
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();
Но, к сожалению, это та же проблема. Файл загружается нормально, но обратный вызов выполнения вызывается только несколько раз. Он сразу достигает 100%, затем мне нужно немного подождать (в зависимости от размера файла), прежде чем я получу ответ 2xx от сервера. Так что я не думаю, что это конкретная проблема DIO.
Я думаю, что похоже, что на самом деле он не показывает прогресс загрузки, а скорее прогресс файла, читаемого в клиентскую сторону потока. Затем поток загружается на сервер, и это то, чего вы ждете (конечно, в зависимости от размера файла), даже если прогресс показывает 100%.
6 ответов
Похоже, это ошибка dio
, Я вижу два способа обойти это:
Просто добавьте число к своему круговому прогрессу, он скажет
99%
(обрезать остальное), чтобы пользователи знали, что это еще не сделано, так как100%
произойдет только когда вы получите1.0
как результат).вместо этого используйте пакет http. в
http
пакет какStreamedRequest
что даст вам более точный контроль над тем, что отправляется, чтобы вы могли отразить это в своем пользовательском интерфейсе.
Я нашел разрешение.
Просто используйте
file.openRead()
как запрос
data
параметр в то время как
PUT
файл.
За
MultipartFile
может быть, он может использовать
MultipartFile(file.openRead(), file.lenthSync())
. Но это я не проверял, может кто-нибудь попробовать, если надо.
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)');
},
);
Обратный вызов прогресса теперь гладкий.
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)
Я столкнулся с той же проблемой. Я перепробовал десятки приемов, но ничего не вышло. Мне пришлось заменить MultipartFile.fromBytes на MultipartFile.fromFile. У меня это сработало.
вы должны использоватьformData
какMap
нетFormData.fromMap()
.
и вы должны поставитьoptions
нравиться :
Map formData ={
your data here
};
var client = new Dio();
await client.put(
url,
data: formData,
options: Options(
headers: {
HttpHeaders.acceptHeader: "json/application/json",
HttpHeaders.contentTypeHeader: "application/x-www-form-urlencoded"
},
),
onSendProgress: (int sent, int total) {
final progress = sent / total;
print('progress: $progress ($sent/$total)');
},
);
Так просто в этом примере
function dioProcess(processfunction){
var client = new dio.Dio();
await client.put(
url,
data: formData,
options: dio.Options(
headers: headers,
),
onSendProgress: processfunction
}
Применение
dioProcess((int sent, int total) {
final progress = sent / total;
print('progress: $progress ($sent/$total)');
},)
1. == использование функции ниже работает: ==
1.use MultipartFile.fromFile(file.path)
2.use MultipartFile.fromFileSync(file.path, filename: basename(file.path))
эти проценты тоже. Например, 10%, 20%, 50%, 75% и т. Д.
2. == использование функции ниже не работает: ==
1. MultipartFile.fromBytes(fileData)
Как видите, он прыгает от 0% до 100%.
пример: я использую await MultipartFile.fromFile(file.path), onSendProgress работает нормально.
Future<OSSResponse> postObjectWithFile(
File file,
String bucketName,
String fileKey, {
ProgressCallback onSendProgress,
ObjectACL acl = ObjectACL.inherited,
}) async {
assert(acl != null, "ACL不能为空");
final OSSCredential credential = await credentialProvider.getCredential();
FormData formData = FormData.fromMap({
'Filename': fileKey,
'key': fileKey,
'policy': SignUtil.getBase64Policy(),
'OSSAccessKeyId': credential.accessKeyId,
'success_action_status': '200',
'signature': SignUtil.getSignature(credential.accessKeySecret),
'x-oss-security-token': credential.securityToken,
'x-oss-object-acl': acl.parameter,
'file': await MultipartFile.fromFile(file.path),//change code
});
final dio = DioUtil.getDio();
final res = await dio.post(
'https://$bucketName.$endpoint',
options: Options(
responseType: ResponseType.plain,
),
data: formData,
onSendProgress: (int sent, int total) {
final progress = sent / total;
print('progress: $progress ($sent/$total)');
},
);
return OSSResponse(res.statusCode, res.statusMessage, fileKey);
}
onSendProgress работает.