mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-04-11 19:28:30 +00:00
2.6 KiB
2.6 KiB
title, order, status
| title | order | status |
|---|---|---|
| S05. 大きなファイルをストリーミングで返す | 24 | draft |
巨大なファイルやリアルタイムに生成されるデータをレスポンスとして返したいとき、全体をメモリに載せるのは現実的ではありません。Response::set_content_provider()を使うと、データをチャンクごとに生成しながら送れます。
サイズがわかっている場合
svr.Get("/download", [](const httplib::Request &req, httplib::Response &res) {
size_t total_size = get_file_size("large.bin");
res.set_content_provider(
total_size, "application/octet-stream",
[](size_t offset, size_t length, httplib::DataSink &sink) {
auto data = read_range_from_file("large.bin", offset, length);
sink.write(data.data(), data.size());
return true;
});
});
ラムダが呼ばれるたびにoffsetとlengthが渡されるので、その範囲だけ読み込んでsink.write()で送ります。メモリには常に少量のチャンクしか載りません。
ファイルをそのまま返す
ただファイルを返すだけなら、set_file_content()のほうがずっと簡単です。
svr.Get("/download", [](const httplib::Request &req, httplib::Response &res) {
res.set_file_content("large.bin", "application/octet-stream");
});
内部でストリーミング送信をしてくれるので、大きなファイルでも安心です。Content-Typeを省略すれば、拡張子から自動で判定されます。
サイズが不明な場合はチャンク転送
リアルタイムに生成されるデータなど、サイズが事前にわからないときはset_chunked_content_provider()を使います。HTTP chunked transfer-encodingとして送信されます。
svr.Get("/events", [](const httplib::Request &req, httplib::Response &res) {
res.set_chunked_content_provider(
"text/plain",
[](size_t offset, httplib::DataSink &sink) {
auto chunk = produce_next_chunk();
if (chunk.empty()) {
sink.done(); // 送信終了
return true;
}
sink.write(chunk.data(), chunk.size());
return true;
});
});
データがもう無くなったらsink.done()を呼んで終了します。
Note: プロバイダラムダは複数回呼ばれます。キャプチャする変数のライフタイムに気をつけてください。必要なら
std::shared_ptrなどで包みましょう。
ファイルダウンロードとして扱いたい場合はS06. ファイルダウンロードレスポンスを返すを参照してください。