Trudno mi znaleźć dokumentację lub przykłady implementacji wskaźnika postępu przesyłania za pomocą funkcji pobierania .
To jedyne odniesienie, jakie do tej pory znalazłem , które stwierdza:
Zdarzenia postępu to funkcja wysokiego poziomu, która na razie nie zostanie pobrana. Możesz utworzyć własny, patrząc na
Content-Length
nagłówek i używając strumienia przekazującego do monitorowania odebranych bajtów.Oznacza to, że możesz jawnie obsługiwać odpowiedzi bez
Content-Length
inaczej. I oczywiście, nawet jeśliContent-Length
istnieje, może to być kłamstwo. Dzięki strumieniom możesz obsługiwać te kłamstwa, jak chcesz.
Jak napisać „przekazujący strumień do monitorowania wysłanych bajtów”? Jeśli to robi jakąkolwiek różnicę, próbuję to zrobić, aby zasilić przesyłanie obrazów z przeglądarki do Cloudinary .
UWAGA : Ja nie interesuje się biblioteki Cloudinary JS , ponieważ zależy to od jQuery i moja aplikacja nie robi. Interesuje mnie tylko przetwarzanie strumienia niezbędne do zrobienia tego z natywnym javascriptem i fetch
polyfillem Githuba.
źródło
Odpowiedzi:
Strumienie zaczynają lądować na platformie internetowej ( https://jakearchibald.com/2016/streams-ftw/ ), ale to dopiero początek.
Wkrótce będziesz w stanie dostarczyć strumień jako treść żądania, ale otwarte pytanie dotyczy tego, czy zużycie tego strumienia dotyczy przesłanych bajtów.
Poszczególne przekierowania mogą powodować retransmisję danych do nowej lokalizacji, ale strumienie nie mogą się ponownie „restartować”. Możemy to naprawić, zmieniając treść w wywołanie zwrotne, które można wywołać wiele razy, ale musimy mieć pewność, że ujawnienie liczby przekierowań nie jest wyciekiem bezpieczeństwa, ponieważ byłby to pierwszy raz na platformie, gdy JS mógłby wykryć to.
Niektórzy zastanawiają się, czy w ogóle sensowne jest powiązanie zużycia strumienia z przesłanymi bajtami.
Krótko mówiąc: nie jest to jeszcze możliwe, ale w przyszłości będzie to obsługiwane przez strumienie lub jakiś rodzaj wywołania zwrotnego wyższego poziomu przekazanego do
fetch()
.źródło
Moim rozwiązaniem jest użycie axios , które całkiem dobrze to obsługuje:
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, //}) })
Mam przykład wykorzystania tego w reakcji na githubie.
źródło
axios
używafetch
lubXMLHttpRequest
pod maską?axios
nie używa się gofetch
pod maską i nie ma takiego wsparcia. Dosłownie tworzę to teraz dla nich, więc.Nie sądzę, żeby to było możliwe. Projekt stanowi:
(stara odpowiedź):
Pierwszy przykład w rozdziale Fetch API daje wgląd w to, jak:
Oprócz tego, że używają
Promise
konstruktora antipattern , możesz zobaczyć, żeresponse.body
jest to strumień, z którego możesz czytać bajt po bajcie za pomocą czytnika, i możesz uruchomić zdarzenie lub zrobić, co chcesz (np. Zarejestrować postęp) dla każdego z nich.Jednak specyfikacja strumieni nie wydaje się być całkowicie ukończona i nie mam pojęcia, czy to już działa w jakiejkolwiek implementacji pobierania.
źródło
fetch
. Interesują mnie wskaźniki postępu przesyłania pliku.Promise
konstruktor nie jest konieczny.Response.body.getReader()
zwraca aPromise
. Zobacz, jak rozwiązać błąd Uncaught Range podczas pobierania plikugetReader
nie zwraca obietnicy. Nie mam pojęcia, co to ma wspólnego z postem, do którego utworzyłeś łącze..getReader()
,.read()
metoda zwraca aPromise
. To właśnie próbowałem przekazać. Link ma nawiązywać do założenia, że jeśli postęp można sprawdzić pod kątem pobierania, postęp można sprawdzić pod kątem przesyłania. Stwórz wzór, który zwraca oczekiwany rezultat w znacznym stopniu; to jest postęp wfetch()
przesyłaniu. Nie znaleźli sposób na lub obiekt w jsfiddle, prawdopodobnie brakuje czegoś prostego. Testowanie przy wysyłaniu pliku bardzo szybko, bez naśladowania warunków sieciowych; choć właśnie zapamiętany .echo
Blob
File
localhost
Network throttling
Aktualizacja: zgodnie z przyjętą odpowiedzią jest to teraz niemożliwe. ale poniższy kod rozwiązał nasz problem przez jakiś czas. Powinienem dodać, że przynajmniej musieliśmy przejść do korzystania z biblioteki opartej na XMLHttpRequest.
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'); }
dzięki temu linkowi: https://jakearchibald.com/2016/streams-ftw/
źródło
content-length
! == długość ciała. Gdy używana jest kompresja http (typowa dla dużych pobrań), długość zawartości to rozmiar po kompresji http, a długość to rozmiar po wyodrębnieniu pliku.bytesReceived
staje się większy niżtotal
Ponieważ żadna z odpowiedzi nie rozwiązuje problemu.
Ze względu na implementację możesz wykryć prędkość wysyłania za pomocą niewielkiej początkowej porcji o znanym rozmiarze, a czas przesyłania można obliczyć za pomocą długości treści / szybkości wysyłania. Możesz wykorzystać ten czas jako oszacowanie.
źródło
Możliwym obejściem byłoby użycie
new Request()
konstruktora, a następnie sprawdzeniaRequest.bodyUsed
Boolean
atrybutuaby określić, czy strumień jest
distributed
Zwraca połączenie
fetch()
Promise
z wewnątrz.then()
do rekurencyjnego.read()
wywołaniaReadableStream
kiedyRequest.bodyUsed
jest równetrue
.Uwaga: podejście nie odczytuje bajtów,
Request.body
ponieważ bajty są przesyłane strumieniowo do punktu końcowego. Ponadto przesyłanie może zakończyć się na długo przed zwróceniem pełnej odpowiedzi do przeglądarki.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)); }
źródło
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}%`; }
źródło
Kluczową częścią jest ReadableStream ≪ obj_response .body≫.
Próba:
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() ))
Możesz przetestować uruchomienie go na ogromnej stronie, np. Https://html.spec.whatwg.org/ i https://html.spec.whatwg.org/print.pdf . CtrlShiftJ i załaduj kod w.
(Testowane w Chrome).
źródło