From b5525ad021436b1c134587f3858ba69f95258b8a Mon Sep 17 00:00:00 2001 From: zsxxsz Date: Sun, 5 Jul 2015 22:51:31 +0800 Subject: [PATCH] add some functions in class string, HttpServletResponse --- acl_cpp_vc2012.sln | 38 +++++++++++++- .../httpd_download/http_servlet.cpp | 51 ++++++++++++++----- .../httpd_download_vc2012.vcxproj | 4 +- app/wizard_demo/httpd_download/main.cpp | 2 +- .../acl_cpp/http/HttpServletResponse.hpp | 16 ++++++ lib_acl_cpp/include/acl_cpp/stdlib/string.hpp | 14 +++++ lib_acl_cpp/src/http/HttpServletResponse.cpp | 8 +++ lib_acl_cpp/src/stdlib/string.cpp | 12 +++++ 8 files changed, 127 insertions(+), 18 deletions(-) diff --git a/acl_cpp_vc2012.sln b/acl_cpp_vc2012.sln index 2f289fb78..9e3069728 100644 --- a/acl_cpp_vc2012.sln +++ b/acl_cpp_vc2012.sln @@ -524,6 +524,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pkg_client", "lib_acl_cpp\s {FE724EF7-3763-4E78-BDF5-BCBC075719FD} = {FE724EF7-3763-4E78-BDF5-BCBC075719FD} EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "wizard_demo", "wizard_demo", "{1C02A405-3726-4410-80DF-91D60736352B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "httpd_download", "app\wizard_demo\httpd_download\httpd_download_vc2012.vcxproj", "{1A38C49B-F712-4115-980B-6DBD632D25CF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Mixed Platforms = Debug|Mixed Platforms @@ -2493,6 +2497,36 @@ Global {094E23DA-F097-4510-9977-0995B8502197}.Template|Win32.ActiveCfg = DebugDll|Win32 {094E23DA-F097-4510-9977-0995B8502197}.Template|Win32.Build.0 = DebugDll|Win32 {094E23DA-F097-4510-9977-0995B8502197}.Template|x64.ActiveCfg = DebugDll|Win32 + {1A38C49B-F712-4115-980B-6DBD632D25CF}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {1A38C49B-F712-4115-980B-6DBD632D25CF}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {1A38C49B-F712-4115-980B-6DBD632D25CF}.Debug|Win32.ActiveCfg = Debug|Win32 + {1A38C49B-F712-4115-980B-6DBD632D25CF}.Debug|Win32.Build.0 = Debug|Win32 + {1A38C49B-F712-4115-980B-6DBD632D25CF}.Debug|x64.ActiveCfg = Debug|x64 + {1A38C49B-F712-4115-980B-6DBD632D25CF}.Debug|x64.Build.0 = Debug|x64 + {1A38C49B-F712-4115-980B-6DBD632D25CF}.DebugDll|Mixed Platforms.ActiveCfg = DebugDll|Win32 + {1A38C49B-F712-4115-980B-6DBD632D25CF}.DebugDll|Mixed Platforms.Build.0 = DebugDll|Win32 + {1A38C49B-F712-4115-980B-6DBD632D25CF}.DebugDll|Win32.ActiveCfg = DebugDll|Win32 + {1A38C49B-F712-4115-980B-6DBD632D25CF}.DebugDll|Win32.Build.0 = DebugDll|Win32 + {1A38C49B-F712-4115-980B-6DBD632D25CF}.DebugDll|x64.ActiveCfg = DebugDll|x64 + {1A38C49B-F712-4115-980B-6DBD632D25CF}.DebugDll|x64.Build.0 = DebugDll|x64 + {1A38C49B-F712-4115-980B-6DBD632D25CF}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {1A38C49B-F712-4115-980B-6DBD632D25CF}.Release|Mixed Platforms.Build.0 = Release|Win32 + {1A38C49B-F712-4115-980B-6DBD632D25CF}.Release|Win32.ActiveCfg = Release|Win32 + {1A38C49B-F712-4115-980B-6DBD632D25CF}.Release|Win32.Build.0 = Release|Win32 + {1A38C49B-F712-4115-980B-6DBD632D25CF}.Release|x64.ActiveCfg = Release|x64 + {1A38C49B-F712-4115-980B-6DBD632D25CF}.Release|x64.Build.0 = Release|x64 + {1A38C49B-F712-4115-980B-6DBD632D25CF}.Releasedll|Mixed Platforms.ActiveCfg = ReleaseDll|Win32 + {1A38C49B-F712-4115-980B-6DBD632D25CF}.Releasedll|Mixed Platforms.Build.0 = ReleaseDll|Win32 + {1A38C49B-F712-4115-980B-6DBD632D25CF}.Releasedll|Win32.ActiveCfg = ReleaseDll|Win32 + {1A38C49B-F712-4115-980B-6DBD632D25CF}.Releasedll|Win32.Build.0 = ReleaseDll|Win32 + {1A38C49B-F712-4115-980B-6DBD632D25CF}.Releasedll|x64.ActiveCfg = ReleaseDll|x64 + {1A38C49B-F712-4115-980B-6DBD632D25CF}.Releasedll|x64.Build.0 = ReleaseDll|x64 + {1A38C49B-F712-4115-980B-6DBD632D25CF}.Template|Mixed Platforms.ActiveCfg = DebugDll|Win32 + {1A38C49B-F712-4115-980B-6DBD632D25CF}.Template|Mixed Platforms.Build.0 = DebugDll|Win32 + {1A38C49B-F712-4115-980B-6DBD632D25CF}.Template|Win32.ActiveCfg = DebugDll|Win32 + {1A38C49B-F712-4115-980B-6DBD632D25CF}.Template|Win32.Build.0 = DebugDll|Win32 + {1A38C49B-F712-4115-980B-6DBD632D25CF}.Template|x64.ActiveCfg = DebugDll|x64 + {1A38C49B-F712-4115-980B-6DBD632D25CF}.Template|x64.Build.0 = DebugDll|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2508,6 +2542,7 @@ Global {128791D9-6787-471C-9A0C-0DCAE53F4911} = {324E7B70-E11C-4A39-A11F-4BB2A8E70064} {DC7E75E5-FE42-4529-9D9A-19D240B8727B} = {324E7B70-E11C-4A39-A11F-4BB2A8E70064} {1F3F3884-7F9B-4BA1-8CA3-61E6758A7193} = {324E7B70-E11C-4A39-A11F-4BB2A8E70064} + {37A14D60-8196-4BCA-9B1A-48302446AFAF} = {324E7B70-E11C-4A39-A11F-4BB2A8E70064} {BE48C8C9-2F6D-450B-9D42-4666E9257C8A} = {324E7B70-E11C-4A39-A11F-4BB2A8E70064} {A76344E8-D8EF-4E0D-9A01-73DB25B5B76E} = {324E7B70-E11C-4A39-A11F-4BB2A8E70064} {2AAD256A-E891-4CFA-BA5F-70DDA2CC63AE} = {324E7B70-E11C-4A39-A11F-4BB2A8E70064} @@ -2517,7 +2552,6 @@ Global {C799E376-2F01-4877-AF18-7F3CC8B95792} = {324E7B70-E11C-4A39-A11F-4BB2A8E70064} {14531A45-383C-4CCF-870B-B5C867A314F2} = {324E7B70-E11C-4A39-A11F-4BB2A8E70064} {B6BE4E77-F69D-43B0-BE8A-77A8AA61A89E} = {324E7B70-E11C-4A39-A11F-4BB2A8E70064} - {37A14D60-8196-4BCA-9B1A-48302446AFAF} = {324E7B70-E11C-4A39-A11F-4BB2A8E70064} {956FA905-A77F-41FE-A4BE-C3BD3B5B3E83} = {02535D96-1693-4AA3-B650-B22DC776FDC2} {225D0BFA-64D7-4F8F-951E-36A494CF6178} = {02535D96-1693-4AA3-B650-B22DC776FDC2} {ADE6F714-BD52-432D-AB86-62FA59AB6746} = {02535D96-1693-4AA3-B650-B22DC776FDC2} @@ -2562,6 +2596,7 @@ Global {D232833B-57A9-4167-B37C-A4594953F93D} = {3CC8D45A-8E3F-4F5C-A7DF-4D8027E4EA9C} {84376B60-FF20-4FD0-967E-C568FC2FBC53} = {3CC8D45A-8E3F-4F5C-A7DF-4D8027E4EA9C} {C8535C82-3DCB-472E-9E8F-DA769D9375B1} = {3CC8D45A-8E3F-4F5C-A7DF-4D8027E4EA9C} + {1C02A405-3726-4410-80DF-91D60736352B} = {3CC8D45A-8E3F-4F5C-A7DF-4D8027E4EA9C} {3C74E826-5E72-48A1-9DFD-777F4A7C9E1F} = {D232833B-57A9-4167-B37C-A4594953F93D} {B59C7CB6-5708-4522-B833-CEB160AA4817} = {D232833B-57A9-4167-B37C-A4594953F93D} {2DABFAD1-114B-4F96-9185-DC0C56A3662D} = {C799E376-2F01-4877-AF18-7F3CC8B95792} @@ -2588,5 +2623,6 @@ Global {067E9700-A518-4747-AE70-0C6F433AE478} = {B6BE4E77-F69D-43B0-BE8A-77A8AA61A89E} {2425D8B1-FE96-4D7F-BDB0-48C932935D96} = {B6BE4E77-F69D-43B0-BE8A-77A8AA61A89E} {AFC106A8-C78A-43C6-BA55-1B7EB541F559} = {B6BE4E77-F69D-43B0-BE8A-77A8AA61A89E} + {1A38C49B-F712-4115-980B-6DBD632D25CF} = {1C02A405-3726-4410-80DF-91D60736352B} EndGlobalSection EndGlobal diff --git a/app/wizard_demo/httpd_download/http_servlet.cpp b/app/wizard_demo/httpd_download/http_servlet.cpp index 358b8315f..16de7c605 100644 --- a/app/wizard_demo/httpd_download/http_servlet.cpp +++ b/app/wizard_demo/httpd_download/http_servlet.cpp @@ -68,6 +68,8 @@ bool http_servlet::doPost(acl::HttpServletRequest& req, req.getSession().setAttribute("sid", "xxxxxx"); sid = req.getSession().getAttribute("sid"); */ + + // 调试:将客户端请求头记录在日志中 acl::string hdr; req.getClient()->sprint_header(hdr); logger("request head:\r\n%s\r\n", hdr.c_str()); @@ -79,6 +81,7 @@ bool http_servlet::doPost(acl::HttpServletRequest& req, return transfer_file(req, res, range_from, range_to); } +// 普通下载过程 bool http_servlet::transfer_file(acl::HttpServletRequest& req, acl::HttpServletResponse& res) { @@ -99,11 +102,18 @@ bool http_servlet::transfer_file(acl::HttpServletRequest& req, bool keep_alive = req.isKeepAlive(); + acl::string hdr_entry; + acl::string filename; + filename.basename(in.file_path()); // 从文件全路径中提取文件名 + hdr_entry.format("attachment;filename=\"%s\"", filename.c_str()); + // 设置 HTTP 响应头中的字段 res.setStatus(200) .setKeepAlive(keep_alive) .setContentLength(fsize) - .setContentType("application/octet-stream"); + .setContentType("application/octet-stream") + // 设置 HTTP 头中的文件名 + .setHeader("Content-Disposition", hdr_entry.c_str()); acl::string hdr; res.getHttpHeader().build_response(hdr); @@ -129,7 +139,8 @@ bool http_servlet::transfer_file(acl::HttpServletRequest& req, // 支持断点续传的数据传输过程 bool http_servlet::transfer_file(acl::HttpServletRequest& req, - acl::HttpServletResponse& res, long long range_from, long long range_to) + acl::HttpServletResponse& res, + long long range_from, long long range_to) { if (range_from < 0) return reply(req, res, 400, "invalid range_from: %lld", @@ -175,19 +186,23 @@ bool http_servlet::transfer_file(acl::HttpServletRequest& req, bool keep_alive = req.isKeepAlive(); + acl::string hdr_entry; + acl::string filename; + filename.basename(in.file_path()); // 从文件全路径中提取文件名 + hdr_entry.format("attachment;filename=\"%s\"", filename.c_str()); + // 设置 HTTP 响应头中的字段 - res.setStatus(206) // 响应状态必须为 206 + res.setStatus(206) // 响应状态 206 表示部分数据 .setKeepAlive(keep_alive) // 是否保持长连接 .setContentLength(length) // 实际要传输的数据长度 - .setContentType("application/octet-stream"); // 数据类型 - - // 设置分段传输数据的范围 - res.getHttpHeader() - // 设置本次传输区间 - .set_range(range_from, range_to > 0 ? range_to : fsize - 1) - // 设置数据总长度 - .set_range_total(fsize); - + .setContentType("application/octet-stream") // 数据类型 + // 设置 HTTP 头中的文件名 + .setHeader("Content-Disposition", hdr_entry.c_str()) + // 设置本次传输区间的起始偏移位置及数据的总长度 + .setRange(range_from, range_to > 0 + ? range_to : fsize - 1, fsize); + + // 调试:将 HTTP 响应头记在本地日志中 acl::string hdr; res.getHttpHeader().build_response(hdr); logger("response head:\r\n%s\r\n", hdr.c_str()); @@ -196,9 +211,13 @@ bool http_servlet::transfer_file(acl::HttpServletRequest& req, int ret; size_t size; + // 从文件指定位置读取数据,并将数据传输给客户端 while (!in.eof() && length > 0) { - size = sizeof(buf) > (size_t) length ? (size_t) length : sizeof(buf); + size = sizeof(buf) > (size_t) length ? + (size_t) length : sizeof(buf); + + // 第三个参数为 false 表示仅进行一次读操作,不必待缓冲区满后再返回 ret = in.read(buf, size, false); if (ret == -1) { @@ -212,6 +231,10 @@ bool http_servlet::transfer_file(acl::HttpServletRequest& req, } if (length != 0) + { + logger_error("read file failed"); return false; - return true; + } + + return true && keep_alive; } diff --git a/app/wizard_demo/httpd_download/httpd_download_vc2012.vcxproj b/app/wizard_demo/httpd_download/httpd_download_vc2012.vcxproj index cf9f4646d..457ed073e 100644 --- a/app/wizard_demo/httpd_download/httpd_download_vc2012.vcxproj +++ b/app/wizard_demo/httpd_download/httpd_download_vc2012.vcxproj @@ -35,7 +35,7 @@ - {58FE3581-C997-4BD5-9AC6-AEEB54A43D2C} + {1A38C49B-F712-4115-980B-6DBD632D25CF} Win32Proj httpd_download @@ -362,4 +362,4 @@ copy ..\..\..\dist\lib\win64\lib_protocol_d.dll $(OutDir) /Y - + \ No newline at end of file diff --git a/app/wizard_demo/httpd_download/main.cpp b/app/wizard_demo/httpd_download/main.cpp index 18ee29f74..596b811e7 100644 --- a/app/wizard_demo/httpd_download/main.cpp +++ b/app/wizard_demo/httpd_download/main.cpp @@ -51,7 +51,7 @@ int main(int argc, char* argv[]) acl::log::stdout_open(true); // 监听的地址列表,格式:ip:port1,ip:port2,... - const char* addrs = "127.0.0.1:8888"; + const char* addrs = ":8888"; printf("listen on: %s\r\n", addrs); // 测试时设置该值 > 0 则指定服务器处理客户端连接过程的 diff --git a/lib_acl_cpp/include/acl_cpp/http/HttpServletResponse.hpp b/lib_acl_cpp/include/acl_cpp/http/HttpServletResponse.hpp index 9a462d59d..393a8344e 100644 --- a/lib_acl_cpp/include/acl_cpp/http/HttpServletResponse.hpp +++ b/lib_acl_cpp/include/acl_cpp/http/HttpServletResponse.hpp @@ -84,6 +84,22 @@ public: */ HttpServletResponse& setHeader(const char* name, int value); + /** + * 对于分区下载,调用本函数设置数据下载的偏移位置(下标从 0 开始) + * @param from {http_off_t} 数据区间起始偏移位置(下标从 0 开始计算) + * @param to {http_off_t} 数据区间结束位置(该值需小于总数据长度) + * @param total {http_off_t} 总数据长度,当数据源为一个静态文件时该值应 + * 等于该文件的总长度大小 + * @return {HttpServletResponse&} + */ +#if defined(_WIN32) || defined(_WIN64) + HttpServletResponse& setRange(__int64 from, + __int64 to, __int64 total); +#else + HttpServletResponse& setRange(long long from, + long long to, long long total); +#endif + /** * 设置 HTTP 响应头中的状态码:1xx, 2xx, 3xx, 4xx, 5xx * @param status {int} HTTP 响应状态码, 如:200 diff --git a/lib_acl_cpp/include/acl_cpp/stdlib/string.hpp b/lib_acl_cpp/include/acl_cpp/stdlib/string.hpp index 2bd363926..ef95da2ff 100644 --- a/lib_acl_cpp/include/acl_cpp/stdlib/string.hpp +++ b/lib_acl_cpp/include/acl_cpp/stdlib/string.hpp @@ -1101,6 +1101,20 @@ public: */ string& hex_decode(const char* s, size_t len); + /** + * 从文件全路径中提取文件名 + * @param path {const char*} 文件全路径字符串,非空字符串 + * @return {string&} 当前对象的引用 + */ + string& basename(const char* path); + + /** + * 从文件全路径中提取文件所在目录 + * @param path {const char*} 文件全路径字符串,非空字符串 + * @return {string&} 当前对象的引用 + */ + string& dirname(const char* path); + /** * 将 32 位有符号整数转为字符串存(内部使用了线程局部变量) * @param n {int} 32 位有符号整数 diff --git a/lib_acl_cpp/src/http/HttpServletResponse.cpp b/lib_acl_cpp/src/http/HttpServletResponse.cpp index d878c79dd..eeb9b66fb 100644 --- a/lib_acl_cpp/src/http/HttpServletResponse.cpp +++ b/lib_acl_cpp/src/http/HttpServletResponse.cpp @@ -95,6 +95,14 @@ HttpServletResponse& HttpServletResponse::setStatus(int status) return *this; } +HttpServletResponse& HttpServletResponse::setRange( + http_off_t from, http_off_t to, http_off_t total) +{ + header_->set_range(from, to); + header_->set_range_total(total); + return *this; +} + HttpServletResponse& HttpServletResponse::setCgiMode(bool on) { header_->set_cgi_mode(on); diff --git a/lib_acl_cpp/src/stdlib/string.cpp b/lib_acl_cpp/src/stdlib/string.cpp index c9f682c4a..3e84380bd 100644 --- a/lib_acl_cpp/src/stdlib/string.cpp +++ b/lib_acl_cpp/src/stdlib/string.cpp @@ -1403,6 +1403,18 @@ string& string::hex_decode(const char* s, size_t len) return *this; } +string& string::basename(const char* path) +{ + (void) acl_sane_basename(vbf_, path); + return *this; +} + +string& string::dirname(const char* path) +{ + (void) acl_sane_dirname(vbf_, path); + return *this; +} + static void dummy_free(void*) { }