-
Notifications
You must be signed in to change notification settings - Fork 376
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
如何搭建server端文件下载服务? #644
Comments
coro_http_server server(10, 8090);
server.set_static_res_dir("", "");
server.sync_start(); 这样就可以下载执行文件所在目录的文件了。不知道你的代码怎么写的,另外是用的最新代码吗? |
@qicosmos 写法跟你的一样,能下载my_test_file.txt, a.txt, b.txt, c.txt。手动创建了d.txt,但是不能下载。麻烦看下: ` async_simple::coro::Lazy byte_ranges_download() { } int main() { |
这个是静态文件下载服务器,目录里面的文件不能是新创建的,即要求文件不可变。 |
@qicosmos 请问是指参考set_static_res_dir这个函数实现动态增加文件可下载吗? |
不是,需要自己写代码实现,类似于这个函数:https://github.com/qicosmos/cinatra/blob/master/include/cinatra/coro_http_server.hpp#L398 |
具体可以在用户群里讨论一下。 |
好的 感谢 |
hi 把你的问题如何解决的在这里发一下吧。 |
@qicosmos 如下处理: std::ifstream file(wav_filename, std::ios::binary); // 读取文件全部内容到字符串流 std::string content = buffer.str(); // 使用读取到的数据进行操作 |
为啥不用静态文件下载的那个方法去做呢,不需要自己写代码去做这个吧。 |
@qicosmos 静态文件下载是只能下载文件夹中已有的文件吧?(接收到请求前已有的文件) |
是的,那个只能下载已有的文件。 |
对于静态文件服务器来说设置一下format type就行了。 coro_http_server server(1, 9001);
server.set_static_res_dir("", "");
server.set_file_resp_format_type(file_resp_format_type::chunked); // 设置为chunked格式下载
server.sync_start(); 自己写代码读文件按chunked格式发送数据参考这个例子: 调用begin_chunked() 开始chunked流传输; |
如果产生数据很慢的话,需要将其异步化,如果生成数据有异步回调接口的话,则使用async_simple Promise 将异步回调转换成协程: async_simple::Promise<std::string> p;
gen_data([&]{
// after generate data
std::string data = generate();
p.setValue(std::move(data));
});
co_await p.getFuture(); 如果生成数据没有异步接口,则可以通过cinatra 提供的线程池将生成数据放到线程池中然后co_await 数据生成: auto data = co_await coro_io::post([]{
return generate();
});
co_await resp.get_conn()->write_chunked(data); |
仿set_static_res_dir写个静态目录 server.set_static_dir("/static/(.*)", "./static"); 添加源码 template <typename... Aspects>
void set_static_dir(std::string_view uri_suffix = "",
std::string_view file_path = "./static",
Aspects &&...aspects) {
bool has_double_dot = (file_path.find("..") != std::string::npos) ||
(uri_suffix.find("..") != std::string::npos);
if (has_double_dot) {
CINATRA_LOG_ERROR << "invalid file path: " << file_path;
std::exit(1);
}
bool has_regex = uri_suffix.find("(.*)") != std::string::npos;
if (!has_regex) {
CINATRA_LOG_ERROR << "invalid uri_suffix: " << uri_suffix;
std::exit(1);
}
set_http_handler<cinatra::GET>(
std::string{uri_suffix},
[this, &uri_suffix, &file_path](
coro_http_request &req,
coro_http_response &resp) -> async_simple::coro::Lazy<void> {
std::string file_name;
std::string url = std::string{req.get_url()};
std::smatch match;
if (std::regex_match(url, match,
std::regex(std::string{uri_suffix}))) {
file_name = match[1].str();
}
std::string static_dir_;
if (!file_path.empty()) {
static_dir_ =
fs::absolute(std::filesystem::path(file_path)).string();
}
else {
static_dir_ = fs::absolute(fs::current_path()).string();
}
file_name = fs::absolute(static_dir_ + "/" + file_name).string();
std::string_view extension = get_extension(file_name);
std::string_view mime = get_mime_type(extension);
auto range_str = req.get_header_value("Range");
std::string content;
detail::resize(content, chunked_size_);
coro_io::coro_file in_file{};
in_file.open(file_name, std::ios::in);
if (!in_file.is_open()) {
resp.set_status_and_content(status_type::not_found,
file_name + "not found");
co_return;
}
size_t file_size = fs::file_size(file_name);
if (format_type_ == file_resp_format_type::chunked &&
range_str.empty()) {
resp.set_format_type(format_type::chunked);
bool ok;
if (ok = co_await resp.get_conn()->begin_chunked(); !ok) {
co_return;
}
while (true) {
auto [ec, size] =
co_await in_file.async_read(content.data(), content.size());
if (ec) {
resp.set_status(status_type::no_content);
co_await resp.get_conn()->reply();
co_return;
}
bool r = co_await resp.get_conn()->write_chunked(
std::string_view(content.data(), size));
if (!r) {
co_return;
}
if (in_file.eof()) {
co_await resp.get_conn()->end_chunked();
break;
}
}
}
else {
auto pos = range_str.find('=');
if (pos != std::string_view::npos) {
range_str = range_str.substr(pos + 1);
bool is_valid = true;
auto ranges =
parse_ranges(range_str, fs::file_size(file_name), is_valid);
if (!is_valid) {
resp.set_status(status_type::range_not_satisfiable);
co_return;
}
assert(!ranges.empty());
if (ranges.size() == 1) {
// single part
auto [start, end] = ranges[0];
in_file.seek(start, std::ios::beg);
size_t part_size = end + 1 - start;
int status = (part_size == file_size) ? 200 : 206;
std::string content_range = "Content-Range: bytes ";
content_range.append(std::to_string(start))
.append("-")
.append(std::to_string(end))
.append("/")
.append(std::to_string(file_size))
.append(CRCF);
auto range_header = build_range_header(
mime, file_name, std::to_string(part_size), status,
content_range);
resp.set_delay(true);
bool r = co_await req.get_conn()->write_data(range_header);
if (!r) {
co_return;
}
co_await send_single_part(in_file, content, req, resp,
part_size);
}
else {
// multiple ranges
resp.set_delay(true);
std::string file_size_str = std::to_string(file_size);
size_t content_len = 0;
std::vector<std::string> multi_heads =
build_part_heads(ranges, mime, file_size_str, content_len);
auto range_header = build_multiple_range_header(content_len);
bool r = co_await req.get_conn()->write_data(range_header);
if (!r) {
co_return;
}
for (int i = 0; i < ranges.size(); i++) {
std::string &part_header = multi_heads[i];
r = co_await req.get_conn()->write_data(part_header);
if (!r) {
co_return;
}
auto [start, end] = ranges[i];
bool ok = in_file.seek(start, std::ios::beg);
if (!ok) {
resp.set_status_and_content(status_type::bad_request,
"invalid range");
co_await resp.get_conn()->reply();
co_return;
}
size_t part_size = end + 1 - start;
std::string_view more = CRCF;
if (i == ranges.size() - 1) {
more = MULTIPART_END;
}
r = co_await send_single_part(in_file, content, req, resp,
part_size, more);
if (!r) {
co_return;
}
}
}
co_return;
}
auto range_header =
build_range_header(mime, file_name, std::to_string(file_size));
resp.set_delay(true);
bool r = co_await req.get_conn()->write_data(range_header);
if (!r) {
co_return;
}
while (true) {
auto [ec, size] =
co_await in_file.async_read(content.data(), content.size());
if (ec) {
resp.set_status(status_type::no_content);
co_await resp.get_conn()->reply();
co_return;
}
r = co_await req.get_conn()->write_data(
std::string_view(content.data(), size));
if (!r) {
co_return;
}
if (in_file.eof()) {
break;
}
}
}
},
std::forward<Aspects>(aspects)...);
} |
我想基于cinatra搭建一个(.wav)文件下载的服务,然后客户端通过curl来发送请求、接收文件。
参考样例 example/main.cpp中的byte_ranges_download()做了精简(删掉了客户端请求的部分)。
能正常运行,通过curl http://127.0.0.1:8090/test_multiple_range.txt 能下载test_multiple_range.txt 文件。
请教一下,该如何下载其他文件,比如通过其他程序生成的wav文件?
另外:server.set_static_res_dir("", ""); 这一句是把当前目录作为返回目录?是不是在这个目录下的所有文件应该都能被下载呢?(但尝试手动创建了a.txt, 使用curl http://127.0.0.1:8090/a.txt提示404错误)
The text was updated successfully, but these errors were encountered: