Getirme için ilerleme göstergeleri yüklensin mi?


111

Getirme özelliğini kullanarak bir yükleme ilerleme göstergesi uygulamaya ilişkin belgeleri veya örnekleri bulmakta zorlanıyorum .

Şimdiye kadar bulduğum tek referans bu :

İlerleme olayları, şimdilik getirilmeyecek yüksek seviyeli bir özelliktir. Content-LengthBaşlığa bakarak ve alınan baytları izlemek için bir geçiş akışı kullanarak kendinizinkini oluşturabilirsiniz .

Bu, yanıtları Content-Lengthfarklı bir şekilde açık bir şekilde ele alabileceğiniz anlamına gelir . Ve elbette, Content-Lengthorada olsa bile bir yalan olabilir. Akarsularla bu yalanları istediğiniz gibi halledebilirsiniz.

Gönderilen "baytları izlemek için bir geçiş akışı" nasıl yazılır? Herhangi bir fark yaratırsa , bunu tarayıcıdan Cloudinary'e görüntü yüklemelerini güçlendirmek için yapmaya çalışıyorum .

NOT : Ben am değil ilgilenen Cloudinary JS kütüphanesinde o jQuery bağlıdır ve benim uygulama değil yaptığı gibi. Yalnızca yerel javascript ve Github'ın fetchçoklu dolgusu ile bunu yapmak için gereken akış işlemeyle ilgileniyorum.


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


Yanıtlar:


48

Akışlar web platformuna girmeye başlıyor ( https://jakearchibald.com/2016/streams-ftw/ ) ancak hala erken günler.

Yakında bir isteğin gövdesi olarak bir akış sağlayabileceksiniz, ancak asıl soru, bu akışın tüketiminin yüklenen baytlarla ilgili olup olmadığıdır.

Belirli yönlendirmeler, verilerin yeni konuma yeniden iletilmesine neden olabilir, ancak akışlar "yeniden başlatılamaz". Bunu, gövdeyi birden çok kez çağrılabilen bir geri aramaya dönüştürerek düzeltebiliriz, ancak yeniden yönlendirme sayısını açığa çıkarmanın bir güvenlik sızıntısı olmadığından emin olmalıyız çünkü bu, JS platformunda ilk kez olabilirdi. bunu tespit et.

Bazıları, akış tüketimini yüklenen baytlara bağlamanın mantıklı olup olmadığını sorguluyor.

Uzun lafın kısası: Bu henüz mümkün değil, ancak gelecekte bu ya akışlarla ya da bir tür üst düzey geri aramayla iletilecek fetch().


7
Çok kötü. Şimdilik bunu kabul ediyorum, ancak bu gerçek olduğunda, umarım başka biri güncellenmiş bir çözüm yayınlar! :)
neezer

1
Güncelleme - akışları kullanarak alma API'sindeki ilerlemeyi gösteriyor - twitter.com/umaar/status/917789464658890753/photo/1
Eitan Peer

2
@EitanPeer Güzel. Benzer bir şey yüklemek için işe yarar mı, örneğin POST?
Michael

6
@EitanPeer Ancak, soru indirmede değil, yüklemede ilerlemeyle ilgili
John Balvin Arias

4
şimdi 2020, neden hala bunu yapmanın bir yolu yok :(
MHA15

26

Benim çözümüm, bunu oldukça iyi destekleyen aksiyoları kullanmak :

      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,
        //})
      })

Bunu github'da react'te kullanmak için bir örneğim var.


2
Bu benim de çözümümdü. Axios kalıba çok iyi uyuyor gibi görünüyor.
Jason Rice

1
Kaputun altında mı yoksa axioskullanıyor mu ? fetchXMLHttpRequest
Dai

4
XMLHttpRequest. Bunu yerel tepki için kullanıyorsanız, XMLHttpRequest'in, getirmeye kıyasla büyük json yanıtlarını çözümlemede ÇOK ÇOK yavaş göründüğüne dikkat edin (yaklaşık 10 kat daha yavaş ve tüm ui iş parçacığını dondurur).
Cristiano Coelho

35
Soruyu cevaplamıyor! Soru "y'de x'i nasıl yaparsınız?" "z'de x yap" demek kabul edilebilir bir cevap değildir.
Derek Henderson

5
Bu soruya cevap vermiyor, özellikle de başlık altında axioskullanılmadığı fetchve böyle bir desteği olmadığı için. Kelimenin tam anlamıyla şimdi onlar için yazıyorum .
sgammon

9

Güncelleme: Kabul edilen yanıtın artık imkansız olduğunu söylediği gibi. ancak aşağıdaki kod bir süre sorunumuzu çözdü. En azından XMLHttpRequest'e dayalı bir kitaplık kullanmaya geçmemiz gerektiğini eklemeliyim.

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');
}

bu bağlantı sayesinde: https://jakearchibald.com/2016/streams-ftw/


4
Güzel, ama yüklemeler için de geçerli mi?
kernel

@kernel Bulmaya çalıştım ama yapamadım. ve bunu yüklemek için de yapmanın bir yolunu bulmayı seviyorum.
Hosseinmp76

3
content-length! == vücut uzunluğu. Http sıkıştırması kullanıldığında (büyük indirmeler için yaygındır), içerik uzunluğu http sıkıştırmasından sonraki boyuttur, uzunluk ise dosya ayıklandıktan sonraki boyuttur.
Ferrybig

2
Kodunuz, içerik başlığı uzunluğunun, getirmenin indireceği bayt miktarını belirttiğini varsayar. Bu her zaman doğru değildir, bu nedenle kodunuz bytesReceiveddaha büyük hale geldiği için kullanıcıya ilerlemeyi gösteremeztotal
Ferrybig

2
Dahası, tarayıcı bile gerçek içerik uzunluğunu önceden bilmiyor. Tek alacağınız bir sıkıştırma sonrası ilerleme göstergesidir. Örneğin, eşit olmayan bir şekilde dağıtılmış sıkıştırma oranına sahip bir zip dosyası indiriyorsanız (bazı dosyalar rastgele, bazıları düşük entropidir) ilerleme göstergesinin ciddi şekilde çarpık olduğunu fark edeceksiniz.
elslooo

7

Bunun mümkün olduğunu sanmıyorum. Taslak şu şekildedir:

İlerleme isteğinde bulunulduğunda şu anda eksiktir [ XHR ile karşılaştırıldığında ]


(eski cevap): API Getirme bölümündeki
ilk örnek, aşağıdakilerin nasıl yapılacağına dair bazı bilgiler verir:

Vücut verilerini aşamalı olarak almak istiyorsanız:

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

Dışında kullanımlarını Promiseyapıcı antipattern , bunu görebiliyorum response.bodyBir Reader kullanarak bayt bayt okuyabilir hangi bir Akış olduğunu ve bir olay yangın veya benzeri her ne Onların her için (örneğin ilerleme log) yapabilirsiniz.

Ancak, Akışlar özelliği tam olarak bitmiş gibi görünmüyor ve bunun herhangi bir getirme uygulamasında zaten çalışıp çalışmadığını bilmiyorum.


14
Bu örneği doğru okursam, bu, üzerinden bir dosya indirmek için olacaktır fetch. Dosya yüklemek için ilerleme göstergeleriyle ilgileniyorum .
neezer

Oops, bu alıntı bayt alma hakkında konuşuyor ve bu da kafamı karıştırdı.
Bergi

1
@Bergi Note, Promisekurucu gerekli değildir. Response.body.getReader()döndürür a Promise. Bkz Yakalanamayan RangeError çözmek için nasıl zaman indirme büyük boy json
guest271314

3
@ guest271314 evet, zaten alıntı kaynağında bunu düzelttim . Ve hayır, getReaderbir söz vermez. Bunun bağladığınız gönderiyle ne ilgisi olduğu hakkında hiçbir fikrim yok.
Bergi

Evet @Bergi, haklısın .getReader()'ın .read()yöntem döndürür Promise. Aktarmaya çalışılan buydu. Bağlantı, ilerleme indirme için kontrol edilebilirse, ilerlemenin yükleme için kontrol edilebileceğini ima etmek içindir. Beklenen sonucu kayda değer ölçüde döndüren bir model oluşturun; bu fetch()yükleme için bir ilerleme . Jsfiddle'da echobir nesne Blobveya bir yol Filebulamadım, muhtemelen basit bir şeyi kaçırdım. localhostAğ koşullarını taklit etmeden dosya yüklerken çok hızlı test etme; sadece hatırladı Network throttling.
misafir271314

4

Cevapların hiçbiri sorunu çözmediğinden.

Yalnızca uygulama uğruna, yükleme hızını bilinen boyutta bazı küçük başlangıç parçalarıyla tespit edebilirsiniz ve yükleme süresi içerik uzunluğu / yükleme hızı ile hesaplanabilir. Bu zamanı tahmin olarak kullanabilirsiniz.


3
Gerçek zamanlı bir çözümü beklerken kullanmak için çok akıllıca, güzel bir numara :)
Magix


2

Olası bir geçici çözüm, yapıcıyı kullanmak ve new Request()ardından Request.bodyUsed Booleanözniteliği kontrol etmektir.

bodyUsedÖzniteliğin alıcı true döndürmesi gerekir disturbedaksi takdirde ve yanlış.

akışın olup olmadığını belirlemek için distributed

BodyKarışımı uygulayan bir nesnenin disturbedeğer bodyboş olmadığı ve onun streamolduğu söylenir disturbed.

Dönüş fetch() Promiseiçinden .then()özyinelemeli zincirlenmiş .read()bir çağrısına ReadableStreamzaman Request.bodyUsedeşittir true.

Not, yaklaşım, baytlar Request.bodyuç noktaya akıtılırken baytlarını okumaz . Ayrıca, herhangi bir yanıt tarayıcıya tam olarak döndürülmeden önce yükleme tamamlanabilir.

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

}

-2
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}%`;
}

Yanıtın tamamı için Benjamin Gruenbaum'a bir kredi vermek istiyorum. Çünkü dersinden öğrendim.
Leon Gilyadov

@LeonGilyadov Ders her yerde çevrimiçi olarak erişilebilir mi? Kaynağa bir bağlantı güzel olurdu.
Mark Amery

@MarkAmery İşte burada: youtube.com/watch?v=Ja8GKkxahCo (ders İbranice verildi)
Leon Gilyadov

13
Soru indirmekle değil, yüklemekle ilgili.
sarneeh

getirme ilerlemesiyle ilgili sorun, yüklemek istediğiniz
zamandır (

-7

Anahtar parçasıdır ReadableStream « obj_response .body».

Örneklem:

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

Bunu büyük bir sayfada çalıştırmayı test edebilirsiniz, örneğin https://html.spec.whatwg.org/ ve https://html.spec.whatwg.org/print.pdf . CtrlShiftJ ve kodu yükleyin.

(Chrome'da test edildi.)


1
Bu cevap eksi puan alıyor ama kimse neden eksi puan verdiğini açıklamıyor - bu yüzden +1
veriyorum

6
Yüklemeyle alakalı olmadığı için benden -1 alıyor .
Brad

-1 aldığına inanıyorum çünkü küçültülmüş javascript sürümünü yazıyor gibi görünüyor
TomasJ
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.