""" 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('/') @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, "内部服务器错误")