feat(api): 添加 MinIO 文件下载支持并优化流处理

添加了对 MinIO 存储文件的直接下载支持,包括:
- 新增 urlparse 和 Config 导入用于 URL 解析和配置读取
- 集成 s3_client 扩展用于 MinIO 对象存储操作
- 实现同源文件检测逻辑,判断是否为 MinIO 存储的文件
- 添加 MinIO 文件流式下载功能,支持大文件处理
- 优化流处理过程中的资源管理,确保请求和文件对象正确关闭
- 增强安全性,验证同源策略防止任意 URL 访问
```
This commit is contained in:
公司git 2026-04-26 10:39:46 +08:00
parent bdbec1a310
commit 4ece904c25

View File

@ -1,5 +1,7 @@
from flask import Blueprint, request, jsonify, session, current_app
from extensions import db, redis_client
from urllib.parse import unquote, urlparse
from config import Config
from extensions import db, redis_client, s3_client
from models import User, SavedPrompt
from middlewares.auth import login_required
from services.logger import system_logger
@ -244,12 +246,39 @@ def download_proxy():
return jsonify({"error": "缺少 URL 参数"}), 400
try:
parsed_url = urlparse(url)
public_prefix = Config.MINIO["public_url"].rstrip("/") + "/"
is_same_origin_file = (
parsed_url.path.startswith(public_prefix)
and (not parsed_url.scheme or parsed_url.netloc == request.host)
)
if is_same_origin_file:
object_key = unquote(parsed_url.path[len(public_prefix):])
file_obj = s3_client.get_object(Bucket=Config.MINIO["bucket"], Key=object_key)
headers = {
"Content-Type": file_obj.get("ContentType") or "application/octet-stream",
"Content-Disposition": f'attachment; filename="{filename}"',
"Cache-Control": "private, max-age=0",
}
def generate_minio_stream():
try:
for chunk in file_obj["Body"].iter_chunks(chunk_size=4096):
yield chunk
finally:
file_obj["Body"].close()
return current_app.response_class(generate_minio_stream(), headers=headers)
req, headers = get_remote_file_stream(url)
headers["Content-Disposition"] = f'attachment; filename="{filename}"'
def generate_stream():
try:
for chunk in req.iter_content(chunk_size=4096):
yield chunk
finally:
req.close()
return current_app.response_class(generate_stream(), headers=headers)
except Exception as e: