s05-stream-response.md 2.2 KB


title: "S05. Stream a Large File in the Response" order: 24

status: "draft"

When the response is a huge file or data generated on the fly, loading the whole thing into memory isn't realistic. Use Response::set_content_provider() to produce data in chunks as you send it.

When the size is known

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 n = std::min(length, size_t(64 * 1024));
      auto data = read_range_from_file("large.bin", offset, n);
      sink.write(data.data(), data.size());
      return true;
    });
});

The lambda is called repeatedly with offset, the position sent so far, and length, the number of bytes still to send. Cap how much you read per call and write it to sink, and only a small chunk sits in memory at any given time.

Just send a file

If you only want to serve a file, set_file_content() is far simpler.

svr.Get("/download", [](const httplib::Request &req, httplib::Response &res) {
  res.set_file_content("large.bin", "application/octet-stream");
});

It streams internally, so even huge files are safe. Omit the Content-Type and it's guessed from the extension.

When the size is unknown — chunked transfer

For data generated on the fly, where you don't know the total size up front, use set_chunked_content_provider(). It's sent with 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(); // done sending
        return true;
      }
      sink.write(chunk.data(), chunk.size());
      return true;
    });
});

Call sink.done() to signal the end.

Note: The provider lambda is called multiple times. Watch out for the lifetime of captured variables — wrap them in a std::shared_ptr if needed.

To serve the file as a download, see S06. Return a file download response.