C10. レスポンスをストリーミングで受信する
レスポンスのボディをチャンクごとに受け取りたいときは、ContentReceiverを使います。大きなファイルを扱うときはもちろん、NDJSON(改行区切りJSON)やログストリームのように「届いた分だけ先に処理したい」ケースでも便利です。
チャンクごとに処理する
httplib::Client cli("http://localhost:8080"); auto res = cli.Get("/logs/stream", [](const char *data, size_t len) { std::cout.write(data, len); std::cout.flush(); return true; // falseを返すと受信を中止 });
httplib::Client cli("http://localhost:8080"); auto res = cli.Get("/logs/stream", [](const char *data, size_t len) { std::cout.write(data, len); std::cout.flush(); return true; // falseを返すと受信を中止 });
サーバーから届いたデータが、到着した順にラムダに渡されます。コールバックがfalseを返すとダウンロードを途中で止められます。
NDJSONを行単位でパースする
バッファを使って改行区切りのJSONを1行ずつ処理する例です。
std::string buffer; auto res = cli.Get("/events", [&](const char *data, size_t len) { buffer.append(data, len); size_t pos; while ((pos = buffer.find('\n')) != std::string::npos) { auto line = buffer.substr(0, pos); buffer.erase(0, pos + 1); if (!line.empty()) { auto j = nlohmann::json::parse(line); handle_event(j); } } return true; });
std::string buffer; auto res = cli.Get("/events", [&](const char *data, size_t len) { buffer.append(data, len); size_t pos; while ((pos = buffer.find('\n')) != std::string::npos) { auto line = buffer.substr(0, pos); buffer.erase(0, pos + 1); if (!line.empty()) { auto j = nlohmann::json::parse(line); handle_event(j); } } return true; });
バッファに貯めながら、改行が見つかるたびに1行を取り出してパースします。ストリーミングAPIをリアルタイムに処理する基本パターンです。
Warning:
ContentReceiverを渡すと、res->bodyは空のままになります。ボディは自分でコールバック内で保存するか処理するかしてください。
ダウンロードの進捗を知りたい場合はC11. 進捗コールバックを使うと組み合わせましょう。 Server-Sent Events(SSE)を扱うときはE04. SSEをクライアントで受信するも参考になります。