#include #include #include #include #include #include #ifdef _WIN32 #include #endif namespace fs = std::filesystem; #ifdef _WIN32 std::string u8_to_ansi(const std::string& str) { int wideCharLen = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, nullptr, 0); if (wideCharLen <= 0) { return ""; } std::wstring wideStr(wideCharLen, L'\0'); MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, &wideStr[0], wideCharLen); int gbkLen = WideCharToMultiByte(CP_ACP, 0, wideStr.c_str(), -1, nullptr, 0, nullptr, nullptr); if (gbkLen <= 0) { return ""; } std::string gbkStr(gbkLen, '\0'); WideCharToMultiByte(CP_ACP, 0, wideStr.c_str(), -1, &gbkStr[0], gbkLen, nullptr, nullptr); gbkStr.resize(gbkLen - 1); return gbkStr; } std::string ansi_to_u8(const std::string& str) { int wideCharLen = MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, nullptr, 0); if (wideCharLen <= 0) { return ""; } std::wstring wideStr(wideCharLen, L'\0'); MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, &wideStr[0], wideCharLen); int utf8Len = WideCharToMultiByte(CP_UTF8, 0, wideStr.c_str(), -1, nullptr, 0, nullptr, nullptr); if (utf8Len <= 0) { return ""; } std::string utf8Str(utf8Len, '\0'); WideCharToMultiByte(CP_UTF8, 0, wideStr.c_str(), -1, &utf8Str[0], utf8Len, nullptr, nullptr); utf8Str.resize(utf8Len - 1); return utf8Str; } #endif // 生成文件列表的HTML页面 std::string generate_file_list(const std::string& base_path, const std::string& current_path) { fs::path full_path = fs::path(base_path) / current_path; std::string html = R"( File Browser

File Browser

Current path: /)" + current_path + R"(
    )"; // 添加返回上一级链接(如果不是根目录) if (current_path != "") { fs::path parent_path = fs::path(current_path).parent_path(); std::string parent_link = parent_path.empty() ? "" : parent_path.string(); html += R"(
  • ..
  • )"; } // 遍历目录下的文件和子目录 for (const auto& entry : fs::directory_iterator(full_path)) { std::string filename = entry.path().filename().string(); std::string new_path = (fs::path(current_path) / filename).string(); if (entry.is_directory()) { html += R"(
  • )" + filename + R"(/
  • )"; } else { // 获取文件大小并转换为MB uintmax_t file_size = entry.file_size(); double size_mb = file_size / (1024.0 * 1024.0); char size_str[20]; snprintf(size_str, sizeof(size_str), "%.4f MB", size_mb); html += R"(
  • )" + filename + R"()" + size_str + R"(
  • )"; } } html += R"(
)"; #ifdef _WIN32 html = ansi_to_u8(html); #endif return html; } int main(int argc, char** argv) { if (argc != 3) { std::cout << "Usage: " << argv[0] << " " << std::endl; return -2; } const std::string base_dir(argv[2]); // 基础文件目录 if (!fs::exists(base_dir)) { std::cout << "Base directory does not exist: " << base_dir << std::endl; return -1; } int port = std::stoi(argv[1]); httplib::Server server; // 确保基础目录存在 if (!fs::exists(base_dir)) { fs::create_directory(base_dir); } // 文件浏览路由 server.Get("/browse(.*)", [&base_dir](const httplib::Request& req, httplib::Response& res) { std::string path = req.matches[1].str(); #ifdef _WIN32 path = u8_to_ansi(path); #endif // 移除开头的斜杠 if (!path.empty() && path[0] == '/') { path = path.substr(1); } // 安全检查:防止目录遍历攻击 if (path.find("..") != std::string::npos) { res.set_content("Invalid path", "text/plain"); res.status = 400; return; } fs::path full_path = fs::path(base_dir) / path; if (!fs::exists(full_path)) { res.set_content("Path not found", "text/plain"); res.status = 404; return; } if (!fs::is_directory(full_path)) { res.set_content("Not a directory", "text/plain"); res.status = 400; return; } res.set_header("Content-Type", "text/html; charset=utf-8"); res.set_content(generate_file_list(base_dir, path), "text/html; charset=utf-8"); }); // 文件下载路由 server.Get("/download/(.*)", [&base_dir](const httplib::Request& req, httplib::Response& res) { std::string path = req.matches[1]; #ifdef _WIN32 path = u8_to_ansi(path); #endif // 安全检查:防止目录遍历攻击 if (path.find("..") != std::string::npos) { res.set_content("Invalid path", "text/plain"); res.status = 400; return; } fs::path file_path = fs::path(base_dir) / path; if (!fs::exists(file_path)) { res.set_content("File not found", "text/plain"); res.status = 404; return; } if (fs::is_directory(file_path)) { res.set_content("Cannot download directory", "text/plain"); res.status = 400; return; } // 设置响应头,触发浏览器下载 res.set_header("Content-Type", "application/octet-stream"); res.set_header("Content-Disposition", "attachment; filename=" + file_path.filename().string()); auto file = std::make_shared(file_path, std::ios::binary); if (!*file) { res.status = 500; res.set_content("Failed to open file", "text/plain"); return; } // 获取文件大小 const size_t file_size = fs::file_size(file_path); // 定义分块回调(严格匹配 ContentProvider 签名) auto provider = [file](size_t offset, size_t length, httplib::DataSink& sink) { file->seekg(offset); const size_t chunk_size = std::min(1024 * 1024, length); // 64KB或剩余长度 std::vector buffer(chunk_size); size_t remaining = length; while (remaining > 0 && sink.is_writable() && *file) { size_t read_size = std::min(buffer.size(), remaining); file->read(buffer.data(), read_size); size_t bytes_read = file->gcount(); if (bytes_read > 0) { sink.write(buffer.data(), bytes_read); remaining -= bytes_read; } else { break; } } return true; }; // 定义资源清理回调 auto releaser = [file](bool /*success*/) { file->close(); }; // 调用 set_content_provider res.set_content_provider(file_size, // 文件总大小 "application/octet-stream", // MIME类型 provider, // 数据提供回调 releaser // 资源清理回调 ); }); // 根目录重定向到/browse server.Get("/", [](const httplib::Request& req, httplib::Response& res) { res.set_redirect("/browse"); }); std::cout << "Server running at http://localhost:" << port << std::endl; std::cout << "Access the root path to browse files.\n"; if (!server.listen("0.0.0.0", port)) { std::cerr << "Failed to start server\n"; return -1; } return 0; }