Завантажити показники прогресу для отримання?


99

Я намагаюся знайти документацію або приклади реалізації індикатора ходу завантаження за допомогою вибору .

Це єдина посилання, яку я знайшов поки що , яка говорить:

Події прогресу - особливість високого рівня, яка наразі не надійде. Ви можете створити свій власний, переглянувши Content-Lengthзаголовок та використовуючи прохідний потік для контролю за отриманими байтами.

Це означає, що ви можете явно обробляти відповіді без Content-Lengthіншого. І звичайно, навіть якщо Content-Lengthвона є, це може бути брехнею. За допомогою потоків ви можете обробляти ці брехні, як тільки захочете.

Як я можу написати "надісланий потік для моніторингу байтів", надісланий? Якщо це має якусь різницю, я намагаюся зробити це для завантаження зображень із браузера в Cloudinary .

ПРИМІТКА : Мене не цікавить бібліотека Cloudinary JS , оскільки це залежить від jQuery, а моє додаток - ні. Мене цікавить лише обробка потоків, необхідна для цього за допомогою власного JavaScript та поліфункції Github fetch.


https://fetch.spec.whatwg.org/#fetch-api


4
@Magix Дивіться відмова від плоду : наступне покоління №447
guest271314

Відповіді:


44

Потоки починають висаджуватися на веб-платформу ( https://jakearchibald.com/2016/streams-ftw/ ), але це ще рано.

Незабаром ви зможете надати потік як основу запиту, але відкритим питанням є те, чи стосується споживання цього потоку до завантажених байтів.

Конкретні переадресації можуть призвести до того, що дані будуть повторно передані на нове місце, але потоки не можуть "перезапуститися". Ми можемо це виправити, перетворивши корпус у зворотний виклик, який можна викликати кілька разів, але нам потрібно бути впевненим, що виявлення кількості переадресацій не є витоком безпеки, оскільки це було б вперше на платформі JS виявити це.

Деякі сумніваються, чи має сенс пов’язати споживання потоку з завантаженими байтами.

Короткий виклад історії: це ще неможливо, але в майбутньому це буде вирішуватися або потоками, або передається якийсь зворотний виклик вищого рівня fetch().


7
Дуже погано. Приймаю це зараз, але коли це стане реальністю, я сподіваюся, що хтось ще опублікує оновлене рішення! :)
neezer

1
Оновлення - показ прогресу в API отримання даних за допомогою потоків - twitter.com/umaar/status/917789464658890753/photo/1
Eitan Peer

2
@EitanPeer Nice. Чи буде подібне працювати для завантаження, наприклад, POST?
Майкл

4
@EitanPeer Але питання про прогрес у завантаженні, а не в завантаженні
Джон Балвін Аріас

1
зараз 2020 рік, чому досі немає способу зробити це :(
MHA15

23

Моє рішення - використовувати axios , який досить добре підтримує це:

      axios.request( {
        method: "post", 
        url: "/aaa", 
        data: myData, 
        onUploadProgress: (p) => {
          console.log(p); 
          //this.setState({
            //fileprogress: p.loaded / p.total
          //})
        }


      }).then (data => {
        //this.setState({
          //fileprogress: 1.0,
        //})
      })

Я маю приклад для використання цього в реакції на github.


2
Це було і моє рішення. Аксіос, схоже, дуже добре вписується у форму.
Джейсон Райс

1
Є чи axiosкористь fetchабо XMLHttpRequestпід капотом?
Дай

3
XMLHttpRequest. Якщо ви використовуєте це для реагування на рідну мову, будьте обережні, що здається, що XMLHttpRequest є ДУЖЕ ДУЖЕ розбірковує великі відповіді json порівняно з добуванням (приблизно в 10 разів повільніше, і він заморожує весь потік інтерфейсу).
Кріштіану Коельо

21
Не відповідає на запитання! Якщо питання "як ви робите х у?" сказати "робити x в z замість" - не прийнятна відповідь.
Дерек Хендерсон

3
Це не дає відповіді на питання, тим більше, що axiosне використовується fetchпід кришкою і не має такої підтримки. Я буквально авторингу зараз для них так.
сгаммон

7

Я не думаю, що це можливо. У проекті зазначено:

на даний момент його не вистачає [ порівняно з XHR ], коли мова заходить про запит прогресування


(стара відповідь):
Перший приклад у розділі Fetch API дає деяке розуміння того, як:

Якщо ви хочете отримувати дані тіла поступово:

function consume(reader) {
  var total = 0
  return new Promise((resolve, reject) => {
    function pump() {
      reader.read().then(({done, value}) => {
        if (done) {
          resolve()
          return
        }
        total += value.byteLength
        log(`received ${value.byteLength} bytes (${total} bytes in total)`)
        pump()
      }).catch(reject)
    }
    pump()
  })
}

fetch("/music/pk/altes-kamuffel.flac")
  .then(res => consume(res.body.getReader()))
  .then(() => log("consumed the entire body without keeping the whole thing in memory!"))
  .catch(e => log("something went wrong: " + e))

Крім їх використання Promise конструктора antipattern , ви можете бачити, що response.bodyце Потік, з якого ви можете читати байт за байтом за допомогою Reader, і ви можете запускати подію або робити все, що вам подобається (наприклад, записувати хід) для кожного з них.

Однак, здається, що специфікація Streams не є повністю закінченою, і я не маю поняття, чи це вже працює в будь-якій реалізації.


11
Якщо я правильно прочитав цей приклад, це було б для завантаження файлу через fetch. Мене цікавлять показники прогресу для завантаження файлу.
neezer

На жаль, ця цитата говорить про отримання байтів, що мене бентежило.
Бергі

@ Bergi Примітка: Promiseконструктор не потрібен. Response.body.getReader()повертає a Promise. Дивіться, як вирішити Uncaught RangeError при завантаженні json великих розмірів
guest271314

3
@ guest271314 Так, я це вже виправив у джерелі цитати. І ні, getReaderне повертає обіцянки. Поняття не маю, що це стосується посади, яку ви пов’язали.
Бергі

@Bergi Так, ви правильно .getReader()«s .read()метод повертає Promise. Саме це намагалися передати. Посилання натякає на передумову, що якщо прогрес можна перевірити для завантаження, прогрес можна перевірити на завантаження. Складіть разом шаблон, який значною мірою повертає очікуваний результат; тобто прогрес для fetch()завантаження. Чи не знайшли спосіб або об'єкт в jsfiddle, ймовірно , відсутня що - то просте. Тестування при завантаженні файлу дуже швидко, не імітуючи мережеві умови; хоч щойно згадав . echoBlobFilelocalhostNetwork throttling
гість271314

6

Оновлення: як говорить прийнята відповідь, зараз це неможливо. але наведений нижче код вирішував нашу проблему колись. Додам, що принаймні нам довелося перейти на використання бібліотеки, що базується на XMLHttpRequest.

const response = await fetch(url);
const total = Number(response.headers.get('content-length'));

const reader = response.body.getReader();
let bytesReceived = 0;
while (true) {
    const result = await reader.read();
    if (result.done) {
        console.log('Fetch complete');
        break;
    }
    bytesReceived += result.value.length;
    console.log('Received', bytesReceived, 'bytes of data so far');
}

завдяки цьому посиланню: https://jakearchibald.com/2016/streams-ftw/


2
Добре, але чи стосується він і завантажень?
ядро

@kernel Я намагався це дізнатися, але мені не вдалося це зробити. і мені подобається знайти спосіб це зробити і для завантаження.
Hosseinmp76

Те саме, але поки що мені не надто пощастило знайти / створити функціональний приклад завантаження.
ядро

1
content-length! == довжина тіла. Якщо використовується стиснення http (звичайне для великих завантажень), довжина вмісту - це розмір після стиснення http, тоді як довжина - розмір після вилучення файлу.
Ferrybig

@Ferrybig Я не зрозумів вашу думку. я десь використав рівність?
Hosseinmp76

4

Оскільки жодна з відповідей не вирішує проблему.

Тільки задля впровадження, ви можете виявити швидкість завантаження за допомогою невеликого початкового відрізка відомого розміру, а час завантаження можна обчислити за допомогою довжини вмісту / швидкості завантаження. Ви можете використовувати цей час як оцінку.


3
Дуже розумний, приємний трюк, який ми використовуємо, поки ми чекаємо рішення в реальному часі :)
Magix

13
Занадто ризиковано для мене. Не хотілося б закінчитись так, як панель прогресу файлів Windows копіює
Джек Гіффін

2

Можливим рішенням може бути використання new Request()конструктора, а потім перевірити Request.bodyUsed Booleanатрибут

У bodyUsedГеттера атрибута повинен повертати істину , якщо disturbed, і брехня в іншому випадку.

щоб визначити, чи є потік distributed

Об'єкт, що реалізує Bodyміксин, називається, disturbedякщо bodyне є нульовим, і він streamє disturbed.

Повернути fetch() Promiseзсередини .then()прикутий до рекурсивного .read()дзвінка a, ReadableStreamколи Request.bodyUsedдорівнює true.

Зауважте, підхід не читає байти байтів, Request.bodyоскільки байти передаються до кінцевої точки. Також завантаження може завершитися ще до того, як будь-яка відповідь буде повернута повністю в браузер.

const [input, progress, label] = [
  document.querySelector("input")
  , document.querySelector("progress")
  , document.querySelector("label")
];

const url = "/path/to/server/";

input.onmousedown = () => {
  label.innerHTML = "";
  progress.value = "0"
};

input.onchange = (event) => {

  const file = event.target.files[0];
  const filename = file.name;
  progress.max = file.size;

  const request = new Request(url, {
    method: "POST",
    body: file,
    cache: "no-store"
  });

  const upload = settings => fetch(settings);

  const uploadProgress = new ReadableStream({
    start(controller) {
        console.log("starting upload, request.bodyUsed:", request.bodyUsed);
        controller.enqueue(request.bodyUsed);
    },
    pull(controller) {
      if (request.bodyUsed) {
        controller.close();
      }
      controller.enqueue(request.bodyUsed);
      console.log("pull, request.bodyUsed:", request.bodyUsed);
    },
    cancel(reason) {
      console.log(reason);
    }
  });

  const [fileUpload, reader] = [
    upload(request)
    .catch(e => {
      reader.cancel();
      throw e
    })
    , uploadProgress.getReader()
  ];

  const processUploadRequest = ({value, done}) => {
    if (value || done) {
      console.log("upload complete, request.bodyUsed:", request.bodyUsed);
      // set `progress.value` to `progress.max` here 
      // if not awaiting server response
      // progress.value = progress.max;
      return reader.closed.then(() => fileUpload);
    }
    console.log("upload progress:", value);
    progress.value = +progress.value + 1;
    return reader.read().then(result => processUploadRequest(result));
  };

  reader.read().then(({value, done}) => processUploadRequest({value,done}))
  .then(response => response.text())
  .then(text => {
    console.log("response:", text);
    progress.value = progress.max;
    input.value = "";
  })
  .catch(err => console.log("upload error:", err));

}

-3
const req = await fetch('./foo.json');
const total = Number(req.headers.get('content-length'));
let loaded = 0;
for await(const {length} of req.body.getReader()) {
  loaded = += length;
  const progress = ((loaded / total) * 100).toFixed(2); // toFixed(2) means two digits after floating point
  console.log(`${progress}%`); // or yourDiv.textContent = `${progress}%`;
}

Я хочу надати відповідь Бенджаміну Груенбауму за всю відповідь. Тому що я дізнався це з його лекції.
Леон Гілядов

@LeonGilyadov Чи доступна лекція в Інтернеті де-небудь? Посилання на джерело було б непогано.
Марк Амері

@MarkAmery Ось: youtube.com/watch?v=Ja8GKkxahCo (лекція
читалася

11
Питання про завантаження, а не про завантаження.
sarneeh

проблема з прогресом отримання - це коли ви хочете завантажити (немає проблем із завантаженням)
Kamil Kiełczewski

-4

Ключова частина є ReadableStream « obj_response .body».

Зразок:

let parse=_/*result*/=>{
  console.log(_)
  //...
  return /*cont?*/_.value?true:false
}

fetch('').
then(_=>( a/*!*/=_.body.getReader(), b/*!*/=z=>a.read().then(parse).then(_=>(_?b:z=>z)()), b() ))

Ви можете протестувати його на величезній сторінці, наприклад, https://html.spec.whatwg.org/ та https://html.spec.whatwg.org/print.pdf . CtrlShiftJ та завантажте код у.

(Тестовано на Chrome.)


Ця відповідь отримує мінус бали, але ніхто не пояснює, чому дають мінус бал - тому я даю +1
Kamil Kiełczewski

3
Він отримує -1 від мене, оскільки це не стосується завантаження .
Бред
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.