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, Я вижу два способа обойти это:

  1. Просто добавьте число к своему круговому прогрессу, он скажет 99% (обрезать остальное), чтобы пользователи знали, что это еще не сделано, так как 100% произойдет только когда вы получите 1.0 как результат).

  2. вместо этого используйте пакет 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 работает.

Другие вопросы по тегам