```
feat(api): 添加 MinIO 文件下载支持并优化流处理 添加了对 MinIO 存储文件的直接下载支持,包括: - 新增 urlparse 和 Config 导入用于 URL 解析和配置读取 - 集成 s3_client 扩展用于 MinIO 对象存储操作 - 实现同源文件检测逻辑,判断是否为 MinIO 存储的文件 - 添加 MinIO 文件流式下载功能,支持大文件处理 - 优化流处理过程中的资源管理,确保请求和文件对象正确关闭 - 增强安全性,验证同源策略防止任意 URL 访问 ```
This commit is contained in:
parent
bdbec1a310
commit
4ece904c25
@ -1,5 +1,7 @@
|
|||||||
from flask import Blueprint, request, jsonify, session, current_app
|
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 models import User, SavedPrompt
|
||||||
from middlewares.auth import login_required
|
from middlewares.auth import login_required
|
||||||
from services.logger import system_logger
|
from services.logger import system_logger
|
||||||
@ -244,12 +246,39 @@ def download_proxy():
|
|||||||
return jsonify({"error": "缺少 URL 参数"}), 400
|
return jsonify({"error": "缺少 URL 参数"}), 400
|
||||||
|
|
||||||
try:
|
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)
|
req, headers = get_remote_file_stream(url)
|
||||||
headers["Content-Disposition"] = f'attachment; filename="{filename}"'
|
headers["Content-Disposition"] = f'attachment; filename="{filename}"'
|
||||||
|
|
||||||
def generate_stream():
|
def generate_stream():
|
||||||
for chunk in req.iter_content(chunk_size=4096):
|
try:
|
||||||
yield chunk
|
for chunk in req.iter_content(chunk_size=4096):
|
||||||
|
yield chunk
|
||||||
|
finally:
|
||||||
|
req.close()
|
||||||
|
|
||||||
return current_app.response_class(generate_stream(), headers=headers)
|
return current_app.response_class(generate_stream(), headers=headers)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user