Загрузить индикаторы прогресса для fetch?
Я изо всех сил пытаюсь найти документацию или примеры реализации индикатора прогресса загрузки с помощью fetch.
это единственная ссылка, которую я нашел до сих пор, в которой говорится:
события прогресса-это функция высокого уровня, которая пока не поступит в fetch. Вы можете создать свой собственный, глядя на
Content-Length
заголовок и использование сквозного потока для мониторинга полученных байтов.Это означает, что вы можете явно обрабатывать ответы без
Content-Length
по-разному. И конечно, даже еслиContent-Length
Это может быть ложь. С streams вы можете справиться с этой ложью, как вы хотите.
как бы я написал" сквозной поток для мониторинга отправленных байтов"? Если это имеет какое-то значение, я пытаюсь сделать это, чтобы загрузить изображения из браузера в Cloudinary.
Примечание: я не интересно в библиотека Cloudinary JS, поскольку это зависит от jQuery, а мое приложение-нет. Меня интересует только обработка потока, необходимая для этого с помощью родного javascript и GitHub fetch
polyfill.
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
- это поток, из которого вы можете читать байт за байтом с помощью считывателя, и вы можете запустить событие или сделать все, что хотите (например, зарегистрировать прогресс) для каждого из них.
возможным обходным путем было бы использовать 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}%`;
}