1. 说明
http如果要传输的文件很大,则需要使用到分块传输。使用分块传输的方式,客户端和服务器都不用在内存里保存文件的全部,每次只收发一小部分即可。
在响应报文里用头字段 “Transfer-Encoding: chunked” 表示报文里的 body 部分不是一次性发过来的,而是分成了许多的块(chunk)分批发送。
httplib库提供两种方式将大文件分块发送。分别是set_chunked_content_provider和set_content_provider。
2. 两种方式的区别
如果使用"set_chunked_content_provider",需手动在所有数据写入到sink后调用sink.done(),而set_content_provider则不需要,后者只需将文件大小作为一个参数传递即可。
3. set_chunked_content_provider方式
svr.Get("/test", [=](const httplib::Request& req, httplib::Response& resp)
{
// file path 文件路径
const char* file_path = "D:/Project/test.zip";
// 404 not found, can't open file
if (!std::ifstream(file_path, std::ifstream::binary | std::ifstream::in).good()) {
resp.status = 404;
resp.set_content("404 not found", "text/plain; charset=UTF-8");
return;
}
resp.set_chunked_content_provider("application/octet-stream", [file_path](size_t offset, httplib::DataSink& sink) {
// open file
std::ifstream file_reader(file_path, std::ifstream::binary | std::ifstream::in);
// can't open file, cancel process
if (!file_reader.good())
return false;
// get file size
file_reader.seekg(0, file_reader.end);
size_t file_size = file_reader.tellg();
file_reader.seekg(0, file_reader.beg);
// check offset and file size, cancel process if offset >= file_size
if (offset >= file_size)
return false;
// larger chunk size get faster download speed, more memory usage, more bandwidth usage, more disk I/O usage
const size_t chunk_size = 32 * 1024;
// prepare read size of chunk
size_t read_size = 0;
bool last_chunk = false;
if (file_size - offset > chunk_size) {
read_size = chunk_size;
last_chunk = false;
} else {
read_size = file_size - offset;
last_chunk = true;
}
// allocate temp buffer, and read file chunk into buffer
std::vector<char> buffer;
buffer.reserve(chunk_size);
file_reader.seekg(offset, file_reader.beg);
file_reader.read(&buffer[0], read_size);
file_reader.close();
// write buffer to sink
sink.write(&buffer[0], read_size);
// done after last chunk had write to sink
if (last_chunk)
sink.done();
return true;
});
});
4. set_content_provider方式
svr.Get("/test1", [=](const httplib::Request& req, httplib::Response& resp) {
// open file
const char* file_path = "D:/Project/hf_server/output/win64/bin/Debug/www/test.zip";
std::ifstream file_reader(file_path, std::ifstream::binary | std::ifstream::in);
// 404 not found
if (!file_reader.good()) {
resp.status = 404;
resp.set_content("404 not found", "text/plain; charset=UTF-8");
return;
}
// get file size
file_reader.seekg(0, file_reader.end);
size_t file_size = file_reader.tellg();
file_reader.seekg(0, file_reader.beg);
resp.set_content_provider(file_size,
"application/octet-stream",
[file_path](size_t offset, size_t length, httplib::DataSink& sink) {
// open file
std::ifstream file_reader(file_path, std::ifstream::binary | std::ifstream::in);
// can't open file, cancel process
if (!file_reader.good())
return false;
// get file size
file_reader.seekg(0, file_reader.end);
size_t file_size = file_reader.tellg();
file_reader.seekg(0, file_reader.beg);
// check offset and file size, cancel process if offset >= file_size
if (offset >= file_size)
return false;
// larger chunk size get faster download speed, more memory usage, more bandwidth usage, more disk I/O usage
const size_t chunk_size = 32 * 1024;
// prepare read size of chunk
size_t read_size = 0;
if (file_size - offset > chunk_size){
read_size = chunk_size;
} else {
read_size = file_size - offset;
}
// allocate temp buffer, and read file chunk into buffer
std::vector<char> buffer;
buffer.reserve(chunk_size);
file_reader.seekg(offset, file_reader.beg);
file_reader.read(&buffer[0], read_size);
file_reader.close();
// write buffer to sink
sink.write(&buffer[0], read_size);
return true;
});
});