From 8420dc80574b46ea3f05a69cf414db9a92902b28 Mon Sep 17 00:00:00 2001 From: xia-chu <771730766@qq.com> Date: Fri, 1 Dec 2023 22:04:02 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9EdownloadFile=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=EF=BC=8C=E6=94=AF=E6=8C=81=E4=B8=8B=E8=BD=BD=E4=BB=BB?= =?UTF-8?q?=E6=84=8F=E6=96=87=E4=BB=B6=20(#3069)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 访问该接口将触发on_http_access hook鉴权; 该接口有一定安全风险,请务必在hook中确认访问鉴权url参数以及访问文件路径是否合法 --- postman/ZLMediaKit.postman_collection.json | 32 ++++++++++++++++++++++ server/WebApi.cpp | 24 ++++++++++++++++ src/Http/HttpFileManager.cpp | 3 ++ 3 files changed, 59 insertions(+) diff --git a/postman/ZLMediaKit.postman_collection.json b/postman/ZLMediaKit.postman_collection.json index b8bbd0f7..1111850f 100644 --- a/postman/ZLMediaKit.postman_collection.json +++ b/postman/ZLMediaKit.postman_collection.json @@ -2184,6 +2184,38 @@ } }, "response": [] + }, + { + "name": "下载文件(downloadFile)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{ZLMediaKit_URL}}/index/api/downloadFile?file_path=/path/to/file.ext", + "host": [ + "{{ZLMediaKit_URL}}" + ], + "path": [ + "index", + "api", + "downloadFile" + ], + "query": [ + { + "key": "file_path", + "value": "/path/to/file.ext", + "description": "文件绝对路径,根据文件名生成Content-Type;该接口将触发on_http_access hook" + }, + { + "key": "save_name", + "value": "test", + "description": "浏览器下载文件后保存文件名;可选参数", + "disabled": true + } + ] + } + }, + "response": [] } ], "event": [ diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 58bf5627..32d01823 100755 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -1809,6 +1809,30 @@ void installWebApi() { reader->startReadMP4(0, true, allArgs["file_repeat"]); }); + api_regist("/index/api/downloadFile", [](API_ARGS_MAP_ASYNC) { + CHECK_ARGS("file_path"); + + // 通过on_http_access完成文件下载鉴权,请务必确认访问鉴权url参数以及访问文件路径是否合法 + HttpSession::HttpAccessPathInvoker file_invoker = [allArgs, invoker](const string &err_msg, const string &cookie_path_in, int life_second) mutable { + if (!err_msg.empty()) { + invoker(401, StrCaseMap{}, err_msg); + } else { + StrCaseMap res_header; + auto save_name = allArgs["save_name"]; + if (!save_name.empty()) { + res_header.emplace("Content-Disposition", "attachment;filename=\"" + save_name + "\""); + } + invoker.responseFile(allArgs.getParser().getHeader(), res_header, allArgs["file_path"]); + } + }; + + bool flag = NOTICE_EMIT(BroadcastHttpAccessArgs, Broadcast::kBroadcastHttpAccess, allArgs.getParser(), allArgs["file_path"], false, file_invoker, sender); + if (!flag) { + // 文件下载鉴权事件无人监听,不允许下载 + file_invoker("None http access event listener", "", 0); + } + }); + ////////////以下是注册的Hook API//////////// api_regist("/index/hook/on_publish",[](API_ARGS_JSON){ //开始推流事件 diff --git a/src/Http/HttpFileManager.cpp b/src/Http/HttpFileManager.cpp index 1eaa9ec1..01d3c928 100644 --- a/src/Http/HttpFileManager.cpp +++ b/src/Http/HttpFileManager.cpp @@ -725,6 +725,9 @@ void HttpResponseInvokerImp::responseFile(const StrCaseMap &requestHeader, return; } + // 尝试添加Content-Type + httpHeader.emplace("Content-Type", HttpConst::getHttpContentType(file.data())); + auto &strRange = const_cast(requestHeader)["Range"]; int code = 200; if (!strRange.empty()) {