RAGflow/management/server/routes/files/routes.py

177 lines
6.0 KiB
Python

from flask import jsonify, request, send_file, current_app
from io import BytesIO
from .. import files_bp
from services.files.service import (
get_files_list,
get_file_info,
download_file_from_minio,
delete_file,
batch_delete_files,
get_minio_client
)
@files_bp.route('', methods=['GET', 'OPTIONS'])
def get_files():
"""获取文件列表的API端点"""
if request.method == 'OPTIONS':
return '', 200
try:
current_page = int(request.args.get('currentPage', 1))
page_size = int(request.args.get('size', 10))
name_filter = request.args.get('name', '')
result, total = get_files_list(current_page, page_size, name_filter)
return jsonify({
"code": 0,
"data": {
"list": result,
"total": total
},
"message": "获取文件列表成功"
})
except Exception as e:
return jsonify({
"code": 500,
"message": f"获取文件列表失败: {str(e)}"
}), 500
@files_bp.route('/<string:file_id>/download', methods=['GET', 'OPTIONS'])
def download_file(file_id):
try:
current_app.logger.info(f"开始处理文件下载请求: {file_id}")
document, _, storage_bucket, storage_location = get_file_info(file_id)
if not document:
current_app.logger.error(f"文件不存在: {file_id}")
return jsonify({
"code": 404,
"message": f"文件 {file_id} 不存在",
"details": "文件记录不存在或已被删除"
}), 404
current_app.logger.info(f"文件信息获取成功: {file_id}, 存储位置: {storage_bucket}/{storage_location}")
try:
minio_client = get_minio_client()
current_app.logger.info(f"MinIO客户端创建成功, 准备检查文件: {storage_bucket}/{storage_location}")
obj = minio_client.stat_object(storage_bucket, storage_location)
if not obj:
current_app.logger.error(f"文件对象为空: {storage_bucket}/{storage_location}")
return jsonify({
"code": 404,
"message": "文件内容为空",
"details": "MinIO存储桶中存在文件记录但内容为空"
}), 404
if obj.size == 0:
current_app.logger.error(f"文件大小为0: {storage_bucket}/{storage_location}")
return jsonify({
"code": 404,
"message": "文件内容为空",
"details": "MinIO存储桶中文件大小为0"
}), 404
current_app.logger.info(f"文件检查成功, 大小: {obj.size} 字节, 准备下载")
response = minio_client.get_object(storage_bucket, storage_location)
file_data = response.read()
current_app.logger.info(f"文件读取成功, 大小: {len(file_data)} 字节, 准备发送")
return send_file(
BytesIO(file_data),
mimetype='application/octet-stream',
as_attachment=True,
download_name=document['name']
)
except Exception as e:
current_app.logger.error(f"MinIO操作异常: {str(e)}", exc_info=True)
# 检查是否是连接错误
if "connection" in str(e).lower():
return jsonify({
"code": 503,
"message": "存储服务连接失败",
"details": f"无法连接到MinIO服务: {str(e)}"
}), 503
# 检查是否是权限错误
elif "access denied" in str(e).lower() or "permission" in str(e).lower():
return jsonify({
"code": 403,
"message": "存储服务访问被拒绝",
"details": f"MinIO访问权限错误: {str(e)}"
}), 403
# 其他错误
else:
return jsonify({
"code": 500,
"message": "存储服务异常",
"details": str(e)
}), 500
except Exception as e:
current_app.logger.error(f"文件下载异常: {str(e)}", exc_info=True)
return jsonify({
"code": 500,
"message": "文件下载失败",
"details": str(e)
}), 500
@files_bp.route('/<string:file_id>', methods=['DELETE', 'OPTIONS'])
def delete_file_route(file_id):
"""删除文件的API端点"""
if request.method == 'OPTIONS':
return '', 200
try:
success = delete_file(file_id)
if success:
return jsonify({
"code": 0,
"message": "文件删除成功"
})
else:
return jsonify({
"code": 404,
"message": f"文件 {file_id} 不存在"
}), 404
except Exception as e:
return jsonify({
"code": 500,
"message": f"删除文件失败: {str(e)}"
}), 500
@files_bp.route('/batch', methods=['DELETE', 'OPTIONS'])
def batch_delete_files_route():
"""批量删除文件的API端点"""
if request.method == 'OPTIONS':
return '', 200
try:
data = request.json
file_ids = data.get('ids', [])
if not file_ids:
return jsonify({
"code": 400,
"message": "未提供要删除的文件ID"
}), 400
success_count = batch_delete_files(file_ids)
return jsonify({
"code": 0,
"message": f"成功删除 {success_count}/{len(file_ids)} 个文件"
})
except Exception as e:
return jsonify({
"code": 500,
"message": f"批量删除文件失败: {str(e)}"
}), 500