結論?
少なくともHTTPのレイヤでtransfer-encoding:chunkedやHTTP2のフレーム分割を使用することができる
ある程度ならその単位に分割したデータがfetchに取得させることができる
実際にはそのサイズはTCPレイヤとかの制限も入るはずだが確証はない
HTTP1.1 リクエスト→レスポンスは順番に処理されるの関係chunkedを使用することでHTTPのレイヤで分割してデータを送ることができる
HTTP2 リクエスト右レスポンスを多重に処理することができる
そのためHTTPレベルで分割されたパケット事にヘッダが生成される
HTTPヘッダの種類
https://developer.mozilla.org/ja/docs/Web/HTTP/Reference/Headers
fetch APIで読み取れるヘッダがどのヘッダか
定義上はレスポンスもリクエストも同じインターフェース
https://developer.mozilla.org/ja/docs/Web/API/Headers
そもそもストリームとは?
HTTP1.1で1つのリクエストに1つのレスポンスを送受信する
HTTP2では複数のリクエストを並列に処理できるようになっている
→フレームヘッダが必要になっている
→レスポンスを小分けにする(チャンクにする)方法はHTML1.1でも存在する
async function fetchAndMeasureJsonSize(url) {
let totalBytes = 0;
let startTime = performance.now(); // 処理開始時刻
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const reader = response.body.getReader();
let done, value;
console.log(`--- Streaming data from: ${url} ---`);
while (true) {
({ done, value } = await reader.read());
if (done) {
break; // 全てのデータが読み込まれた
}
// valueはUint8Arrayです
totalBytes += value.byteLength;
console.log(`Received chunk of ${value.byteLength} bytes. Total: ${totalBytes} bytes.`);
}
let endTime = performance.now(); // 処理終了時刻
let duration = (endTime - startTime) / 1000; // 秒に変換
console.log(`--- Download complete! ---`);
console.log(`Total downloaded size: ${totalBytes} bytes`);
console.log(`Time taken: ${duration.toFixed(2)} seconds`);
// 必要であれば、完全に読み込んだデータをJSONとしてパースすることも可能ですが、
// ここではストリームでのサイズ測定が主目的のため割愛します。
// 例: const text = new TextDecoder().decode(buffer); // bufferは全データを結合したもの
// const json = JSON.parse(text);
} catch (error) {
console.error('Fetch error:', error);
}
}
app.get('/chunked-text', (req, res) => {
// ヘッダーの設定 (Content-Type は必要、Transfer-Encoding: chunked はExpress/Node.jsが自動で追加)
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
let count = 0;
const intervalId = setInterval(() => {
count++;
//const dataChunk = `データチャンク #${count} - ${new Date().toLocaleTimeString()}\n`;
const dataChunk = "x".repeat(1024 * 1024); // 1MBのデータチャンクを生成
console.log(`Sending chunk: ${new Date().toLocaleTimeString()}`);
// データチャンクを書き込む
res.write(dataChunk);
if (count >= 5) {
clearInterval(intervalId);
// 全てのデータ送信が完了したら res.end() を呼び出す
res.end('全てのデータ送信が完了しました。\n');
console.log('Response ended.');
}
}, 1000); // 1秒ごとにチャンクを送信
});