--- title: "S07. マルチパートデータをストリーミングで受け取る" order: 26 status: "draft" --- 大きなファイルをアップロードするハンドラを普通に書くと、`req.body`にリクエスト全体が載ってしまいメモリを圧迫します。`HandlerWithContentReader`を使うと、ボディをチャンクごとに受け取れます。 ## 基本の使い方 ```cpp svr.Post("/upload", [](const httplib::Request &req, httplib::Response &res, const httplib::ContentReader &content_reader) { if (req.is_multipart_form_data()) { content_reader( // 各パートのヘッダー [&](const httplib::FormData &file) { std::cout << "name: " << file.name << ", filename: " << file.filename << std::endl; return true; }, // 各パートのボディ(複数回呼ばれる) [&](const char *data, size_t len) { // ここでファイルに書き出すなど return true; }); } else { // 普通のリクエストボディ content_reader([&](const char *data, size_t len) { return true; }); } res.set_content("ok", "text/plain"); }); ``` `content_reader`は2通りの呼び方ができます。マルチパートのときは2つのコールバック(ヘッダー用とデータ用)を渡し、そうでないときは1つのコールバックだけを渡します。 ## ファイルに直接書き出す 大きなファイルをそのままディスクに書き出す例です。 ```cpp svr.Post("/upload", [](const httplib::Request &req, httplib::Response &res, const httplib::ContentReader &content_reader) { std::ofstream ofs; content_reader( [&](const httplib::FormData &file) { if (!file.filename.empty()) { ofs.open("uploads/" + file.filename, std::ios::binary); } return static_cast(ofs); }, [&](const char *data, size_t len) { ofs.write(data, len); return static_cast(ofs); }); res.set_content("uploaded", "text/plain"); }); ``` メモリには常に小さなチャンクしか載らないので、ギガバイト級のファイルでも扱えます。 > **Warning:** `HandlerWithContentReader`を使うと、`req.body`は**空のまま**です。ボディはコールバック内で自分で処理してください。 > クライアント側でマルチパートを送る方法は[C07. ファイルをマルチパートフォームとしてアップロードする](c07-multipart-upload)を参照してください。