web-dev-qa-db-fra.com

Télécharger des indicateurs de progression pour la récupération?

J'ai du mal à trouver de la documentation ou des exemples d'implémentation d'un indicateur de progression du téléchargement à l'aide de fetch

C'est la seule référence que j'ai trouvée jusqu'à présent , qui dit:

Les événements de progression sont une fonctionnalité de haut niveau qui n'arrivera pas dans l'extraction pour le moment. Vous pouvez créer le vôtre en consultant l'en-tête Content-Length et en utilisant un flux direct pour surveiller les octets reçus.

Cela signifie que vous pouvez explicitement gérer les réponses sans Content-Length différemment. Et bien sûr, même si Content-Length est là, cela peut être un mensonge. Avec les flux, vous pouvez gérer ces mensonges comme vous le souhaitez.

Comment pourrais-je écrire "un flux intermédiaire pour surveiller les octets" envoyé? Si cela fait une différence, j'essaie de le faire pour que le téléchargement d'images à partir du navigateur aboutisse à Cloudinary .

NOTE: je suis pas intéressé par la bibliothèque JS Cloudinary , car cela dépend de jQuery et non de mon application. Je ne m'intéresse qu'au traitement de flux nécessaire pour le faire avec javascript natif et fetch polyfill de Github.


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

53
neezer

Les flux commencent à atterrir sur la plate-forme Web ( https://jakearchibald.com/2016/streams-ftw/ ), mais il est encore tôt.

Bientôt, vous pourrez fournir un flux en tant que corps d'une demande, mais la question ouverte est de savoir si la consommation de ce flux est liée aux octets téléchargés.

Des redirections particulières peuvent entraîner la retransmission de données vers le nouvel emplacement, mais les flux ne peuvent pas "redémarrer". Nous pouvons résoudre ce problème en transformant le corps en un rappel qui peut être appelé plusieurs fois, mais nous devons nous assurer que le fait d'exposer le nombre de redirections ne constitue pas une fuite de sécurité, car ce serait la première fois sur la plateforme que JS pourrait détecter que.

Certains se demandent s'il est même logique de lier la consommation de flux aux octets téléchargés.

Bref récit: ce n'est pas encore possible, mais à l'avenir, cela sera géré soit par des flux, soit par un type de rappel de niveau supérieur transmis à fetch().

31
Jaffa The Cake

Je ne pense pas que ce soit possible. Le projet stipule:

il manque actuellement [par rapport à XHR] pour demander la progression


(ancienne réponse):
Le premier exemple du chapitre Fetch API donne un aperçu de la façon de:

Si vous souhaitez recevoir les données du corps progressivement: 

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

Outre l'utilisation du constructeur Promise antipattern , vous pouvez voir que response.body est un flux à partir duquel vous pouvez lire octet par octet à l'aide d'un Reader, et vous pouvez déclencher un événement ou faire ce que vous voulez (par exemple, enregistrer la progression ) pour chacun d’eux.

Cependant, la spécification Streams ne semble pas être complètement terminée, et je ne sais pas si cela fonctionne déjà dans une implémentation d'extraction.

7
Bergi

Ma solution est d'utiliser axios , ce qui supporte plutôt bien ceci: 

      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,
        //})
      })

J'ai des exemples d'utilisation de ceci dans react on github.

6
dwjohnston

Puisque aucune des réponses ne résout le problème. 

Juste pour des raisons de mise en œuvre, vous pouvez détecter la vitesse de téléchargement avec un petit bloc initial de taille connue et le temps de téléchargement peut être calculé avec longueur de contenu/vitesse de téléchargement. Vous pouvez utiliser ce temps comme estimation.

4
Shishir Arora

Une solution possible consisterait à utiliser le constructeur new Request(), puis à vérifier l'attribut Request.bodyUsedBoolean

Le getter de l'attribut bodyUsed doit renvoyer true si disturbed et faux sinon.

pour déterminer si le flux est distributed

Un objet implémentant le mélange Body est dit disturbed si body est non-null et sa stream est disturbed.

Renvoie le fetch()Promise de .then() chaîné à l'appel récursif .read() d'un ReadableStream lorsque Request.bodyUsed est égal à true.

Notez que l'approche ne lit pas les octets du Request.body car ceux-ci sont transmis au noeud final. En outre, le téléchargement pourrait bien se terminer avant que toute réponse ne soit renvoyée intégralement au navigateur. 

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
guest271314
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');
}

grâce à ce lien: https://jakearchibald.com/2016/streams-ftw/

1
Hosseinmp76
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}%`;
}
0
Leon Gilyadov