fetch を使用して、アップロードの進行状況インジケーターを実装するドキュメントまたは例を見つけるのに苦労しています。
これは私がこれまでに見つけた唯一の参照です 、それは述べています:
進行状況イベントは、現在のところフェッチで到着しない高レベルの機能です。
Content-Length
ヘッダーを見て、パススルーストリームを使用して受信したバイトを監視することにより、独自のヘッダーを作成できます。これは、
Content-Length
なしで応答を明示的に処理できることを意味します。そしてもちろん、Content-Length
がそこにあっても嘘になる可能性があります。ストリームを使用すると、これらの嘘を自由に処理できます。
「バイトを監視するためのパススルーストリーム」を送信するにはどうすればよいですか?何らかの違いが生じる場合は、ブラウザーから Cloudinary に画像をアップロードするためにこれを実行しようとしています。
NOTE:私はnotCloudinary JS library に興味があります。これはjQueryに依存しており、私のアプリには依存していません。ネイティブjavascriptとGithubのfetch
polyfillでこれを行うために必要なストリーム処理にのみ興味があります。
ストリームはWebプラットフォームに到着し始めています( https://jakearchibald.com/2016/streams-ftw/ )が、まだ初期段階です。
すぐに、リクエストの本文としてストリームを提供できるようになりますが、未解決の問題は、そのストリームの消費がアップロードされたバイトに関連するかどうかです。
特定のリダイレクトにより、新しい場所にデータが再送信される可能性がありますが、ストリームは「再起動」できません。ボディを複数回呼び出すことができるコールバックに変えることでこれを修正できますが、リダイレクトの回数を公開することはセキュリティリークではないことを確認する必要があります。それを検出します。
ストリームの消費をアップロードされたバイトにリンクするのが理にかなっているかどうか疑問視している人もいます。
簡単に言えば、これはまだ可能ではありませんが、将来的には、ストリーム、またはfetch()
に渡される何らかの高レベルのコールバックによって処理されます。
可能だとは思いません。ドラフトの状態:
進行を要求することになると、現在[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 は完全に終了しているようには見えず、フェッチ実装でこれがすでに機能しているかどうかはわかりません。
答えはどれも問題を解決しないので。
実装のために、アップロード速度を検出できます 既知のサイズの小さな初期チャンクを使用 で、アップロード時間はcontent-length/upload-speedで計算できます。この時間を推定として使用できます。
考えられる回避策は、new Request()
コンストラクターを使用してからRequest.bodyUsed
Boolean
属性をチェックすることです。
bodyUsed
属性のgetterは、disturbed
の場合はtrueを、そうでない場合はfalseを返す必要があります。
ストリームがdistributed
かどうかを判断する
Body
mixinを実装するオブジェクトは、disturbed
がnullでなく、そのbody
がstream
である場合、disturbed
と呼ばれます。
Request.bodyUsed
がPromise
と等しい場合、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));
}
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/
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}%`;
}