フェッチの進行状況インジケーターをアップロードしますか?


99

ドキュメントまたはfetchを使用してアップロードの進行状況インジケーターを実装する例を見つけるのに苦労しています。

これは私がこれまで見つけた唯一のリファレンスであり、次のように述べています。

進行状況イベントは、現時点ではフェッチで到着しない高レベルの機能です。Content-Lengthヘッダーを確認し、パススルーストリームを使用して受信したバイトを監視することにより、独自のものを作成できます。

つまり、Content-Length別の方法なしで明示的に応答を処理できます。そしてもちろん、たとえそれContent-Lengthがあったとしても、それは嘘になる可能性があります。ストリームを使用すると、これらの嘘を好きなように処理できます。

送信された「バイトを監視するためのパススルーストリーム」をどのように記述しますか?それが何らかの違いを生む場合、私はこれを行って、ブラウザからCloudinaryへの画像アップロードを強化しようとしています。

:私はCloudinary JSライブラリ興味がありません。これは、jQueryに依存しており、私のアプリはそうではないためです。私は、ネイティブJavaScriptとGithubのポリフィルでこれを行うために必要なストリーム処理にのみ興味があります。fetch


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


回答:


44

ストリームはWebプラットフォーム(https://jakearchibald.com/2016/streams-ftw/)に着陸し始めていますが、まだ初期の段階です。

すぐにリクエストの本文としてストリームを提供できるようになりますが、未解決の問題は、そのストリームの消費がアップロードされたバイトに関連しているかどうかです。

特定のリダイレクトにより、データが新しい場所に再送信される可能性がありますが、ストリームは「再開」できません。ボディを複数回コールできるコールバックに変えることでこれを修正できますが、JSがプラットフォーム上で初めてになる可能性があるため、リダイレクトの数を公開することがセキュリティリークではないことを確認する必要があります。それを検出します。

一部の人は、ストリームの消費をアップロードされたバイトにリンクすることが理にかなっているのではないかと疑っています。

簡単に言えば、これはまだ不可能ですが、将来的にはストリーム、またはに渡されるある種の高レベルのコールバックのいずれかによって処理されますfetch()


7
残念な。今のところこれを受け入れていますが、これが現実になったときに、誰かが更新されたソリューションを投稿してくれることを願っています!:)
neezer 2016年

@ jaffa-the-cakeニュースはありますか?
mu3

1
更新-ストリームを使用したフェッチAPIの進捗状況を表示-twitter.com/umaar/status/917789464658890753/photo/1
Eitan Peer

2
@EitanPeerニース。POSTなどのアップロードでも同様のことができますか?
マイケル

4
@EitanPeerしかし、問題はダウンロードではなくアップロードの進行についてです
John Balvin Arias

23

私の解決策はこれをかなりうまくサポートする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,
        //})
      })

これをGitHubのReactで使用する例があります。


2
それも私の解決策でした。Axiosは金型に非常によく適合しているようです。
Jason Rice

1
axios使用しているfetchXMLHttpRequest、内部で使用していますか?

3
XMLHttpRequest。ネイティブの反応にこれを使用している場合、XMLHttpRequestは、フェッチと比較した場合、大きなjson応答の解析が非常に遅いようです(約10倍遅く、UIスレッド全体がフリーズします)。
Cristiano Coelho

21
質問に答えません!質問が「xをyにするにはどうすればよいですか?」「代わりにxをzで実行してください」と言っても問題ありません。
デレクヘンダーソン

3
これは質問の答えにはなりません。特に、内部でaxios使用しないためfetch、そのようなサポートはありません。私は文字通りそれ彼らのために今書いています。
sgammon

7

それは可能ではないと思います。草案は述べています:

進行を要求することになると現在(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はリーダーを使用してバイト単位で読み取ることができるストリームであり、イベントを起動したり、それらのすべてに対して好きなことを実行したりできます(進行状況のログなど)。

ただし、Streamsの仕様はまだ完成していないようで、これがフェッチの実装で既に機能しているかどうかはわかりません。


11
ただし、その例を正しく読んだ場合、これはを介してファイルをダウンロードするためのものですfetch。ファイルのアップロードの進行状況インジケータに興味があります。
neezer

おっと、その引用はバイトの受信について話しており、それは私を混乱させました。
Bergi

@Bergi注意、Promiseコンストラクタは必要ありません。Response.body.getReader()を返しますPromise大きなサイズのjsonをダウンロードする際のUncaught RangeErrorの解決方法を
guest271314

3
@ guest271314ええ、私はすでに引用それを修正しました。そして、いいえ、getReader約束を返しません。リンクした投稿とこれがどう関係しているのかわかりません。
Bergi、

はい@Bergi、あなたは正しい.getReader().read()メソッドが返しますPromise。それが伝えようとしていたことです。リンクは、ダウンロードの進行状況を確認できる場合は、アップロードの進行状況を確認できるという前提を暗示しています。予想される結果をかなりの程度返すパターンをまとめます。fetch()アップロードの進捗状況です。道発見していないか、おそらく何かが簡単に欠け、jsfiddleでオブジェクトを。アップロードファイルでのテストは、ネットワークの状態を模倣することなく、非常に高速です。ただ覚えたけど。echoBlobFilelocalhostNetwork throttling
guest271314 2016

6

更新:受け入れられた答えが今は不可能だと言っているように。しかし、以下のコードはしばらくの間私たちの問題を処理しました。少なくとも、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');
}

このリンクのおかげで:https : //jakearchibald.com/2016/streams-ftw/


2
いいですが、アップロードにも適用されますか?
カーネル

@kernel調べてみましたが、できませんでした。アップロードのためにこれを行う方法を見つけるのも好きです。
Hosseinmp76

同じことですが、これまでのところ、機能するアップロードの例を見つける/作成するのはそれほど幸運ではありませんでした。
カーネル

1
content-length!==体の長さ。http圧縮が使用されている場合(大きなダウンロードで一般的)、content-lengthはhttp圧縮後のサイズですが、lengthはファイルが抽出された後のサイズです。
Ferrybig、

@Ferrybig私はあなたのポイントを理解しませんでした。どこかで平等を使用しましたか?
Hosseinmp76

4

答えはどれも問題を解決しないので。

実装のためだけに、既知のサイズの小さな初期チャンクでアップロード速度検出できます。アップロード時間は、content-length / upload-speedで計算できます。この時間は推定として使用できます。


3
リアルタイムソリューションを待つ間、非常に巧妙で素晴らしいトリックを使用します:)
Magix


2

考えられる回避策は、new Request()コンストラクタを利用してからRequest.bodyUsed Boolean属性を確認することです

bodyUsed属性のゲッターがあればtrueを返す必要がありdisturbedそうでない場合は、偽。

ストリームが distributed

Bodyミックスインを実装するオブジェクトは、disturbedif bodyがnullではなく、streamisであるといいdisturbedます。

がと等しいときに、チェーンfetch() Promise内から.then()チェーンの再帰.read()呼び出しに戻ります。ReadableStreamRequest.bodyUsedtrue

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

}

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

答えはすべてベンジャミン・グレンバウムに感謝したい。私は彼の講義からそれを学んだからです。
Leon Gilyadov

@LeonGilyadov講義はオンラインでどこでも利用できますか?ソースへのリンクがいいでしょう。
マークアメリー2018

@MarkAmeryここにあります:youtube.com/watch ?v=Ja8GKkxahCo (講義はヘブライ語で行われました)
Leon Gilyadov

11
問題は、ダウンロードではなくアップロードについてです。
サルニー

フェッチの進行に関する問題は、アップロードしたいときです(ダウンロードには問題ありません)
KamilKiełczewski

-4

キー部分があるReadableStream « obj_response .body»。

サンプル:

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でテスト済み。)


この答えはマイナスポイントを取得しますが、マイナスポイントを与える理由を説明する人はいません。そのため、私は+1を与えます
KamilKiełczewski

3
アップロードとは関係がないので、-1を受け取ります。
ブラッド
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.