web-dev-qa-db-ja.com

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

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

これは私がこれまでに見つけた唯一の参照です 、それは述べています:

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

これは、Content-Lengthなしで応答を明示的に処理できることを意味します。そしてもちろん、Content-Lengthがそこにあっても嘘になる可能性があります。ストリームを使用すると、これらの嘘を自由に処理できます。

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

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


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

70
neezer

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

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

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

ストリームの消費をアップロードされたバイトにリンクするのが理にかなっているかどうか疑問視している人もいます。

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

34
JaffaTheCake

私の解決策は、 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。 のリアクションでこれを使用する例があります

11
dwjohnston

可能だとは思いません。ドラフトの状態:

進行を要求することになると、現在[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 spec は完全に終了しているようには見えず、フェッチ実装でこれがすでに機能しているかどうかはわかりません。

7
Bergi

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

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

4
Shishir Arora

考えられる回避策は、new Request()コンストラクターを使用してからRequest.bodyUsedBoolean属性をチェックすることです。

bodyUsed属性のgetterは、disturbedの場合はtrueを、そうでない場合はfalseを返す必要があります。

ストリームがdistributedかどうかを判断する

Body mixinを実装するオブジェクトは、disturbedがnullでなく、そのbodystreamである場合、disturbedと呼ばれます。

Request.bodyUsedPromiseと等しい場合、ReadableStreamの再帰fetch()呼び出しにチェーンされた.then()内から.read()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));

}
3
guest271314
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
Hosseinmp76
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
Leon Gilyadov