""" 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"})