Upload progress indicators for fetch?
Streams are starting to land in the web platform (https://jakearchibald.com/2016/streams-ftw/) but it's still early days.
Soon you'll be able to provide a stream as the body of a request, but the open question is whether the consumption of that stream relates to bytes uploaded.
Particular redirects can result in data being retransmitted to the new location, but streams cannot "restart". We can fix this by turning the body into a callback which can be called multiple times, but we need to be sure that exposing the number of redirects isn't a security leak, since it'd be the first time on the platform JS could detect that.
Some are questioning whether it even makes sense to link stream consumption to bytes uploaded.
Long story short: this isn't possible yet, but in future this will be handled either by streams, or some kind of higher-level callback passed into fetch()
.
Upload file with Fetch API in Javascript and show progress
This is NOT possible. The reason is the way the Fetch API works.
The fetch
method returns a Promise; the Promise API uses a then
method to which you can attach “success” and “failure” callbacks. Therefore, you can gain access to progress.
Still, don't lose hope! There is a workaround that can do the trick (I found it on github repository of the Fetch API):
you can convert the request to a stream request and then when a response return is just a bitarray of the file content. then you need to collect all of the data and when its end decode it to the file you want
function consume(stream, total = 0) {
while (stream.state === "readable") {
var data = stream.read()
total += data.byteLength;
console.log("received " + data.byteLength + " bytes (" + total + " bytes in total).")
}
if (stream.state === "waiting") {
stream.ready.then(() => consume(stream, total))
}
return stream.closed
}
fetch("/music/pk/altes-kamuffel.flac")
.then(res => consume(res.body))
.then(() => console.log("consumed the entire body without keeping the whole thing in memory!"))
.catch((e) => console.error("something went wrong", e))
How to get progress from Fetch API?
As of right now, you can't. It's not part of the spec yet. There was an issue for it on the spec's repo but it's closed. There's also some discussion on this issue about it.
Long story short, you are probably better off just sticking with xhr
for now unless you want to try some of the polyfills/workarounds in the second issue Iinked.
It's just not supported, and that is also the reason fetch
requests don't show up in the network tab in the devtools untill they've completed.
Polyfill for upload progress of fetch, without stream
Here's our solution which falls back to XHR, while trying to stay close to fetch's signature. Note, that this focuses on the progress, response status and response data are omitted.
interface OnProgress {
(loaded: number, total: number, done: boolean): void;
}
export function fetch(url: string, { method, headers, body }, onProgress: OnProgress): Promise {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(method, url, true);
xhr.upload.addEventListener('progress', e => onProgress(e.loaded, e.total, false));
xhr.upload.addEventListener('loadend', _ => onProgress(undefined, undefined, true));
xhr.onreadystatechange = () => {
if (xhr.readyState === 4)
resolve();
};
xhr.onerror = err => reject(err);
Object.entries(headers).forEach(([name, value]) => xhr.setRequestHeader(name, value));
xhr.send(body);
});
}
Usage example:
import { fetch } from 'my-fetch-progress';
const formData = new FormData();
formData.append('file', file, file.name);
const onProgress = (loaded, total, done) => console.log('onProgress', loaded, total, done);
fetch('/my-upload', {
method: 'POST',
headers: { 'Authorization': `Bearer ...` },
body: formData
}, onProgress)
.then(_ => /*...*/);
Related Topics
Full Text Search in HTML Ignoring Tags/&
Changing Element Style Attribute Dynamically Using JavaScript
Fetch(), How to Make a Non-Cached Request
Html5 Dragleave Fired When Hovering a Child Element
Why JavaScript This.Style[Property] Return an Empty String
Webkit-Based Blurry/Distorted Text Post-Animation Via Translate3D
Force Link to Open in Mobile Safari from a Web App With JavaScript
How to Randomize (Shuffle) a JavaScript Array
Referenceerror: $ Is Not Defined
Jquery Ui Accordion That Keeps Multiple Sections Open
Any Way to Clone Html5 Canvas Element With Its Content
React.Js: Set Innerhtml VS Dangerouslysetinnerhtml
Is It Possible For Flex Items to Align Tightly to the Items Above Them
How to Normalize the Css3 Transition End Events Across Browsers