video_monitor/app/views/StorageView.py
2026-08-30 22:23:12 +08:00

86 lines
3.4 KiB
Python

"""
StorageView 文件下载模块
提供文件下载功能(导出日志、导出配置等场景使用)。
"""
from app.views.ViewsBase import *
from app.utils.LanguageUtils import LANG_VIEWS_T
from django.core import signing
from django.utils.encoding import escape_uri_path
import os
DOWNLOAD_ID_SALT = "monitor.temp-download.v1"
DOWNLOAD_ID_MAX_AGE_SECONDS = 300
ALLOWED_DOWNLOAD_SUFFIXES = frozenset((
".mp4", ".wav", ".jpg", ".png", ".tar", ".xclogs", ".xcsettings", ".xcupdate", ".xcflow",
))
def _safe_temp_download_path(filename, require_exists=True):
if not filename or filename != os.path.basename(filename):
raise ValueError("invalid download file")
if os.path.splitext(filename)[1].lower() not in ALLOWED_DOWNLOAD_SUFFIXES:
raise ValueError("unsupported download file type")
base = os.path.realpath(g_config.storageTempDir)
path = os.path.realpath(os.path.join(base, filename))
if os.path.dirname(path) != base:
raise ValueError("invalid download path")
if require_exists and not os.path.isfile(path):
raise FileNotFoundError("download file not found")
return path
def issue_download_file_id(filename):
"""Return an opaque, short-lived identifier for a server-created temp file."""
_safe_temp_download_path(filename, require_exists=True)
return signing.dumps({"name": filename}, salt=DOWNLOAD_ID_SALT, compress=True)
def resolve_download_file_id(file_id):
data = signing.loads(file_id, salt=DOWNLOAD_ID_SALT, max_age=DOWNLOAD_ID_MAX_AGE_SECONDS)
if not isinstance(data, dict):
raise signing.BadSignature("invalid download id")
filename = str(data.get("name") or "")
return filename, _safe_temp_download_path(filename, require_exists=True)
def api_openInfo(request):
"""查询存储空间信息(原 Storage 模块,已移除)"""
if request.method != 'GET':
return f_responseJson({"code": 0, "msg": LANG_VIEWS_T(request, "msg_method_not_supported")})
__check_ret, __check_msg = f_checkRequestSafe(request)
if not __check_ret:
return f_responseJson({"code": 0, "msg": __check_msg})
g_logger.info("StorageView.api_openInfo() ip:%s" % f_parseRequestIp(request))
return f_responseJson({
"code": 1000,
"msg": "ok",
"info": {
"alarmFolderSize": 0,
"recordFolderSize": 0
}
})
def api_openDownload(request):
"""Download a server-created temp file by a short-lived signed identifier."""
__check_ret, __check_msg = f_checkRequestSafe(request)
if not __check_ret:
return f_responseJson({"code": 0, "msg": __check_msg})
params = f_parseGetParams(request)
file_id = params.get("file_id", "").strip()
try:
if not file_id:
raise signing.BadSignature("missing download id")
filename, filepath = resolve_download_file_id(file_id)
from django.http import FileResponse
response = FileResponse(open(filepath, mode="rb"), content_type="application/octet-stream")
response['Content-Disposition'] = "attachment; filename={};".format(escape_uri_path(filename))
response['Cache-Control'] = "no-store"
return response
except (signing.BadSignature, signing.SignatureExpired, ValueError, FileNotFoundError) as e:
g_logger.warning("StorageView.openDownload() rejected: %s" % str(e))
return f_responseJson({"code": 0, "msg": "invalid or expired download id"})