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 siContent-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.
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()
.
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.
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.
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.
Une solution possible consisterait à utiliser le constructeur new Request()
, puis à vérifier l'attribut Request.bodyUsed
Boolean
Le getter de l'attribut
bodyUsed
doit renvoyer true sidisturbed
et faux sinon.
pour déterminer si le flux est distributed
Un objet implémentant le mélange
Body
est ditdisturbed
sibody
est non-null et sastream
estdisturbed
.
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));
}
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/
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}%`;
}