2016-02-29 16 views
37

Staram się znaleźć dokumentację lub przykłady implementacji wskaźnika postępu przesyłania przy użyciu fetch.Prześlij wskaźniki postępu pobierania?

This is the only reference I've found so far, który stanowi:

wydarzenia Progress są cechą wysoki poziom, że nie przybędzie w sprowadzić do teraz. Możesz tworzyć własne, patrząc na nagłówek Content-Length i używając strumienia tranzytowego do monitorowania odebranych bajtów.

Oznacza to, że można jawnie obsługiwać odpowiedzi bez różnicy w stosunku do Content-Length. I oczywiście, nawet jeśli jest tam Content-Length, może to być kłamstwo. Dzięki strumieniom możesz obsłużyć te kłamstwa, jak tylko chcesz.

Jak napisałbym "strumień tranzytowy do monitorowania bajtów" wysłany? Jeśli robi to jakąkolwiek różnicę, próbuję to zrobić, aby przesyłać obrazy z przeglądarki do Cloudinary.

UWAGA: Jestem nie zainteresowani Cloudinary JS library, ponieważ zależy to od jQuery i moja aplikacja nie robi. Interesuje mnie tylko przetwarzanie strumieniowe niezbędne do tego przy pomocy natywnego javascript i polyfill Github'a fetch.


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

+3

@Magix Patrz [Przerywanie FETCH: The Next Generation # 447]. (Https: // github.com/whatwg/fetch/issues/447) – guest271314

Odpowiedz

23

Strumienie zaczynają lądować na platformie internetowej (https://jakearchibald.com/2016/streams-ftw/), ale to wciąż początek.

Wkrótce będzie można podać strumień jako treść żądania, ale otwarte pytanie brzmi, czy zużycie tego strumienia odnosi się do przesłanych bajtów.

Poszczególne przekierowania mogą spowodować ponowne przesłanie danych do nowej lokalizacji, ale strumienie nie mogą zostać "ponownie uruchomione". Możemy to naprawić, przekształcając ciało w wywołanie zwrotne, które można nazwać wiele razy, ale musimy mieć pewność, że ujawnienie liczby przekierowań nie jest przeciekiem bezpieczeństwa, ponieważ byłby to pierwszy raz na platformie JS. wykryć to.

Niektórzy kwestionują, czy nawet ma sens łączenie 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 przekazywany do fetch().

+4

Szkoda. Akceptuję to na razie, ale kiedy to stanie się rzeczywistością, mam nadzieję, że ktoś inny opublikuje zaktualizowane rozwiązanie! :) – neezer

+0

@ jaffa-the-cake jakiekolwiek wiadomości? – mu3

+1

Aktualizacja - pokazująca postęp z Fetch API za pomocą strumieni - https://twitter.com/umaar/status/917789464658890753/photo/1 –

5

Nie sądzę, że to możliwe. Projekty stany:

jest obecnie brakuje [w porównaniu do XHR], jeśli chodzi o żądania progresji


(odpowiedź stary):
Pierwszy przykład w Fetch API chapter daje pewien wgląd w:

Jeśli chcesz otrzymywać dane ciało stopniowo:

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

Oprócz ich używania Promise constructor antipattern, można zobaczyć, że response.body jest strumień, z którego można odczytać bajt po bajcie za pomocą czytnika, a ty może wystrzelić jakieś wydarzenie lub zrobić cokolwiek chcesz (np zapisz postęp) dla każdego z nich.

Jednak Streams spec nie wydaje się być całkiem skończony, i nie mam pojęcia, czy to już działa w dowolnej implementacji pobierania.

+5

Jeśli jednak przeczytam ten przykład poprawnie, będzie to dotyczyło ** pobrania ** pliku za pomocą 'fetch'. Interesują mnie wskaźniki postępu dla ** przesłania ** pliku. – neezer

+0

Ups, ten cytat mówi o * odbiorze * bajtów, co mnie zdezorientowało. – Bergi

+0

@Bergi Note, Konstruktor 'Obietnica' nie jest konieczny. 'Response.body.getReader()' zwraca 'Promise'. Zobacz [Jak rozwiązać Uncaught RangeError przy pobieraniu dużych plików json] (http://stackoverflow.com/questions/39959467/how-to-solve-uncaugh-rangeerror-when-download-large-size-json) – guest271314

2

Możliwe obejście byłoby wykorzystać new Request() konstruktora następnie sprawdzić Request.bodyUsedBoolean atrybut

getter atrybutu bodyUsed musi zwrócić true jeśli disturbed i false w przeciwnym wypadku.

celu określenia, czy strumień jest distributed

Celem realizacji Body wstawek mówi się disturbed jeśli body jest niezerowe i jego stream jest disturbed.

Przywrócić fetch()Promise od wewnątrz .then() przykuty do rekurencyjnego .read() wezwanie do ReadableStream gdy Request.bodyUsed jest równa true.

Uwaga: podejście nie odczytuje bajtów z Request.body, ponieważ bajty są przesyłane strumieniowo do punktu końcowego. Przesyłanie może zakończyć się również, zanim jakakolwiek odpowiedź zostanie zwrócona w całości 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)); 

} 
3

Ponieważ żadna z odpowiedzi nie rozwiązuje problemu.

Tylko dla celów implementacji, można wykryć prędkość wysyłania with some small initial chunk of known size, a czas ładowania można obliczyć za pomocą długości treści/szybkości wysyłania. Możesz użyć tego czasu jako oszacowania.

+1

Bardzo sprytna, miła sztuczka do wykorzystania, gdy czekamy na rozwiązanie w czasie rzeczywistym :) – Magix

0
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

to nigdy nie zadziała, ponieważ 'ReadableStreamDefaultReader' nie ma własności' byteLength'. – silicakes

+0

Chcę dać kredyt Benjaminowi Gruenbaumowi za całą odpowiedź. Ponieważ nauczyłem się tego z jego wykładu. –