Files
UAV-CO2/src/gasflux/blueprints/download.py
2026-02-05 15:13:54 +08:00

68 lines
2.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Download Blueprint
Handles file download endpoints.
"""
from pathlib import Path
from flask import Blueprint, send_file, current_app
from ..shared import _format_response, log_performance, logger
# Create blueprint
download_bp = Blueprint('download', __name__, url_prefix='/download')
@download_bp.route('/<path:filename>')
@log_performance
def download_file(filename):
"""Download a processed file."""
from flask import request
logger.info(f"Download request for file: {filename} from IP {request.remote_addr}")
try:
# 支持两种路径格式:
# 1. 绝对路径(以 / 开头,如 /full/path/to/file)
# 2. 相对路径(task_id/filename)
if filename.startswith('/'):
# 绝对路径 - 直接使用
file_path = Path(filename)
else:
# 相对路径 - 相对于 OUTPUT_FOLDER
output_folder = Path(current_app.config.get('OUTPUT_FOLDER') or '')
if not output_folder:
logger.error("OUTPUT_FOLDER not configured")
return _format_response(500, "服务器配置错误")
# 解析 task_id/filename 格式
parts = filename.split('/', 1)
if len(parts) != 2:
logger.warning(f"Invalid relative path format: {filename}")
return _format_response(400, "无效的文件路径")
task_id, filename_part = parts
file_path = output_folder / task_id / filename_part
# Security check - ensure file is within output folder
file_path = file_path.resolve()
output_folder = Path(current_app.config.get('OUTPUT_FOLDER') or '').resolve()
if output_folder and not str(file_path).startswith(str(output_folder)):
logger.warning(f"Security violation: Attempted to access file outside output folder: {filename}")
return _format_response(403, "访问被拒绝")
if not file_path.exists():
logger.warning(f"File not found: {filename}")
return _format_response(404, "文件未找到")
if not file_path.is_file():
logger.warning(f"Path is not a file: {filename}")
return _format_response(400, "不是文件")
file_size = file_path.stat().st_size
logger.info(f"Serving file: {filename} ({file_size} bytes)")
return send_file(file_path)
except Exception as e:
logger.error(f"Error serving file {filename}: {str(e)}", exc_info=True)
return _format_response(500, "内部服务器错误")