mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2024-11-25 20:27:34 +08:00
新增downloadFile接口,支持下载限定目录下任意文件 (#3073)
This commit is contained in:
parent
e1f199c986
commit
10c2197e76
@ -15,6 +15,8 @@ secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc
|
|||||||
snapRoot=./www/snap/
|
snapRoot=./www/snap/
|
||||||
#默认截图图片,在启动FFmpeg截图后但是截图还未生成时,可以返回默认的预设图片
|
#默认截图图片,在启动FFmpeg截图后但是截图还未生成时,可以返回默认的预设图片
|
||||||
defaultSnap=./www/logo.png
|
defaultSnap=./www/logo.png
|
||||||
|
#downloadFile http接口可访问文件的根目录,支持多个目录,不同目录通过分号(;)分隔
|
||||||
|
downloadRoot=./www
|
||||||
|
|
||||||
[ffmpeg]
|
[ffmpeg]
|
||||||
#FFmpeg可执行程序路径,支持相对路径/绝对路径
|
#FFmpeg可执行程序路径,支持相对路径/绝对路径
|
||||||
|
@ -2184,6 +2184,38 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"response": []
|
"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": [
|
"event": [
|
||||||
|
@ -72,12 +72,14 @@ const string kApiDebug = API_FIELD"apiDebug";
|
|||||||
const string kSecret = API_FIELD"secret";
|
const string kSecret = API_FIELD"secret";
|
||||||
const string kSnapRoot = API_FIELD"snapRoot";
|
const string kSnapRoot = API_FIELD"snapRoot";
|
||||||
const string kDefaultSnap = API_FIELD"defaultSnap";
|
const string kDefaultSnap = API_FIELD"defaultSnap";
|
||||||
|
const string kDownloadRoot = API_FIELD"downloadRoot";
|
||||||
|
|
||||||
static onceToken token([]() {
|
static onceToken token([]() {
|
||||||
mINI::Instance()[kApiDebug] = "1";
|
mINI::Instance()[kApiDebug] = "1";
|
||||||
mINI::Instance()[kSecret] = "035c73f7-bb6b-4889-a715-d9eb2d1925cc";
|
mINI::Instance()[kSecret] = "035c73f7-bb6b-4889-a715-d9eb2d1925cc";
|
||||||
mINI::Instance()[kSnapRoot] = "./www/snap/";
|
mINI::Instance()[kSnapRoot] = "./www/snap/";
|
||||||
mINI::Instance()[kDefaultSnap] = "./www/logo.png";
|
mINI::Instance()[kDefaultSnap] = "./www/logo.png";
|
||||||
|
mINI::Instance()[kDownloadRoot] = "./www";
|
||||||
});
|
});
|
||||||
}//namespace API
|
}//namespace API
|
||||||
|
|
||||||
@ -1824,6 +1826,57 @@ void installWebApi() {
|
|||||||
// sample_ms设置为0,从配置文件加载;file_repeat可以指定,如果配置文件也指定循环解复用,那么强制开启
|
// sample_ms设置为0,从配置文件加载;file_repeat可以指定,如果配置文件也指定循环解复用,那么强制开启
|
||||||
reader->startReadMP4(0, true, allArgs["file_repeat"]);
|
reader->startReadMP4(0, true, allArgs["file_repeat"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
GET_CONFIG_FUNC(std::set<std::string>, download_roots, API::kDownloadRoot, [](const string &str) -> std::set<std::string> {
|
||||||
|
std::set<std::string> ret;
|
||||||
|
auto vec = toolkit::split(str, ";");
|
||||||
|
for (auto &item : vec) {
|
||||||
|
auto root = File::absolutePath(item, "", true);
|
||||||
|
ret.emplace(std::move(root));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
|
||||||
|
api_regist("/index/api/downloadFile", [](API_ARGS_MAP_ASYNC) {
|
||||||
|
CHECK_ARGS("file_path");
|
||||||
|
auto file_path = allArgs["file_path"];
|
||||||
|
|
||||||
|
if (file_path.find("..") != std::string::npos) {
|
||||||
|
invoker(401, StrCaseMap{}, "You can not access parent directory");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bool safe = false;
|
||||||
|
for (auto &root : download_roots) {
|
||||||
|
if (start_with(file_path, root)) {
|
||||||
|
safe = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!safe) {
|
||||||
|
invoker(401, StrCaseMap{}, "You can not download files outside the root directory");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通过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(), file_path, false, file_invoker, sender);
|
||||||
|
if (!flag) {
|
||||||
|
// 文件下载鉴权事件无人监听,不允许下载
|
||||||
|
invoker(401, StrCaseMap {}, "None http access event listener");
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void unInstallWebApi(){
|
void unInstallWebApi(){
|
||||||
|
@ -725,6 +725,9 @@ void HttpResponseInvokerImp::responseFile(const StrCaseMap &requestHeader,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 尝试添加Content-Type
|
||||||
|
httpHeader.emplace("Content-Type", HttpConst::getHttpContentType(file.data()));
|
||||||
|
|
||||||
auto &strRange = const_cast<StrCaseMap &>(requestHeader)["Range"];
|
auto &strRange = const_cast<StrCaseMap &>(requestHeader)["Range"];
|
||||||
int code = 200;
|
int code = 200;
|
||||||
if (!strRange.empty()) {
|
if (!strRange.empty()) {
|
||||||
|
Loading…
Reference in New Issue
Block a user