Загрузить индикаторы прогресса для fetch?

Я изо всех сил пытаюсь найти документацию или примеры реализации индикатора прогресса загрузки с помощью fetch.

это единственная ссылка, которую я нашел до сих пор, в которой говорится:

события прогресса-это функция высокого уровня, которая пока не поступит в fetch. Вы можете создать свой собственный, глядя на Content-Length заголовок и использование сквозного потока для мониторинга полученных байтов.

Это означает, что вы можете явно обрабатывать ответы без Content-Length по-разному. И конечно, даже если Content-Length Это может быть ложь. С streams вы можете справиться с этой ложью, как вы хотите.

как бы я написал" сквозной поток для мониторинга отправленных байтов"? Если это имеет какое-то значение, я пытаюсь сделать это, чтобы загрузить изображения из браузера в Cloudinary.

Примечание: я не интересно в библиотека Cloudinary JS, поскольку это зависит от jQuery, а мое приложение-нет. Меня интересует только обработка потока, необходимая для этого с помощью родного javascript и GitHub fetch polyfill.


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

7 ответов


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

скоро вы сможете предоставить поток в качестве тела запроса, но открытый вопрос заключается в том, относится ли потребление этого потока к загруженным байтам.

определенные перенаправления могут привести к повторной передаче данных в новое местоположение, но потоки не могут "перезапустить". Мы можем исправить это, превратив тело в обратный вызов, который может быть вызван несколько раз, но мы должны быть уверены, что раскрытие количества перенаправлений не является утечкой безопасности, так как это будет первый раз на платформе JS может обнаружить это.

некоторые задаются вопросом, Имеет ли смысл связывать потребление потока с загруженными байтами.

короче говоря: это пока невозможно, но в будущем это будет обрабатываться либо потоками, либо каким-то более высоким уровнем обратного вызова, переданным в fetch().


Я не думаю, что это возможно. В проекте говорится:

В настоящее время отсутствует [по сравнению с 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 антиобразец конструктор видно, что response.body - это поток, из которого вы можете читать байт за байтом с помощью считывателя, и вы можете запустить событие или сделать все, что хотите (например, зарегистрировать прогресс) для каждого из них.

на Streams spec кажется, не совсем закончено, и я понятия не имею, работает ли это уже в любой реализации выборки.

возможным обходным путем было бы использовать new Request() конструктор после проверки Request.bodyUsed Boolean атрибут

на bodyUsed геттер атрибута должен возвращать true, если disturbed, и false в противном случае.

чтобы определить, является ли поток distributed

объект, реализующий Body миксин считается disturbed если body не-null и stream и disturbed.

вернуть fetch() Promise внутри .then() прикован к рекурсивной .read() вызов 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));

}

поскольку ни один из ответов решить проблему.

просто для реализации, вы можете обнаружить скорость загрузки С небольшим начальным куском известного размера и время загрузки можно рассчитать с помощью content-length / upload-speed. Вы можете использовать это время как оценка.


мое решение-использовать 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,
        //})
      })

У меня есть пример для использования этого в react on github.


основная часть составляет ReadableStream ≪obj_response.тело≫.

пример:

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.)


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}%`;
}