179 lines
6.3 KiB
Python
179 lines
6.3 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, handle_chunk_upload, merge_chunks, upload_files_to_server
|
||
from services.files.utils import FileType
|
||
|
||
UPLOAD_FOLDER = "/data/uploads"
|
||
ALLOWED_EXTENSIONS = {"txt", "pdf", "png", "jpg", "jpeg", "gif", "doc", "docx", "xls", "xlsx"}
|
||
|
||
|
||
def allowed_file(filename):
|
||
return "." in filename and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS
|
||
|
||
|
||
@files_bp.route("/upload", methods=["POST"])
|
||
def upload_file():
|
||
if "files" not in request.files:
|
||
return jsonify({"code": 400, "message": "未选择文件", "data": None}), 400
|
||
|
||
files = request.files.getlist("files")
|
||
upload_result = upload_files_to_server(files)
|
||
|
||
# 返回标准格式
|
||
return jsonify({"code": 0, "message": "上传成功", "data": upload_result["data"]})
|
||
|
||
|
||
@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", "")
|
||
sort_by = request.args.get("sort_by", "create_time")
|
||
sort_order = request.args.get("sort_order", "desc")
|
||
|
||
result, total = get_files_list(current_page, page_size, name_filter, sort_by, sort_order)
|
||
|
||
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}")
|
||
|
||
# 获取文件信息
|
||
file = get_file_info(file_id)
|
||
|
||
if not file:
|
||
current_app.logger.error(f"文件不存在: {file_id}")
|
||
return jsonify({"code": 404, "message": f"文件 {file_id} 不存在", "details": "文件记录不存在或已被删除"}), 404
|
||
|
||
if file["type"] == FileType.FOLDER.value:
|
||
current_app.logger.error(f"不能下载文件夹: {file_id}")
|
||
return jsonify({"code": 400, "message": "不能下载文件夹", "details": "请选择一个文件进行下载"}), 400
|
||
|
||
current_app.logger.info(f"文件信息获取成功: {file_id}, 存储位置: {file['parent_id']}/{file['location']}")
|
||
|
||
try:
|
||
# 从MinIO下载文件
|
||
file_data, filename = download_file_from_minio(file_id)
|
||
|
||
# 创建内存文件对象
|
||
file_stream = BytesIO(file_data)
|
||
|
||
# 返回文件
|
||
return send_file(file_stream, download_name=filename, as_attachment=True, mimetype="application/octet-stream")
|
||
|
||
except Exception as e:
|
||
current_app.logger.error(f"下载文件失败: {str(e)}")
|
||
return jsonify({"code": 500, "message": "下载文件失败", "details": str(e)}), 500
|
||
|
||
except Exception as e:
|
||
current_app.logger.error(f"处理下载请求时出错: {str(e)}")
|
||
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
|
||
|
||
|
||
@files_bp.route("/upload/chunk", methods=["POST"])
|
||
def upload_chunk():
|
||
"""
|
||
处理文件分块上传
|
||
"""
|
||
if "chunk" not in request.files:
|
||
return jsonify({"code": 400, "message": "未选择文件分块", "data": None}), 400
|
||
|
||
chunk = request.files["chunk"]
|
||
chunk_index = request.form.get("chunkIndex")
|
||
total_chunks = request.form.get("totalChunks")
|
||
upload_id = request.form.get("uploadId")
|
||
file_name = request.form.get("fileName")
|
||
parent_id = request.form.get("parent_id")
|
||
|
||
if not all([chunk_index, total_chunks, upload_id, file_name]):
|
||
return jsonify({"code": 400, "message": "缺少必要参数", "data": None}), 400
|
||
|
||
result = handle_chunk_upload(chunk, chunk_index, total_chunks, upload_id, file_name, parent_id)
|
||
|
||
# 检查结果中是否有错误信息
|
||
if result.get("code", 0) != 0:
|
||
# 如果有错误,返回相应的HTTP状态码
|
||
return jsonify(result), result.get("code", 500)
|
||
|
||
return jsonify(result)
|
||
|
||
|
||
@files_bp.route("/upload/merge", methods=["POST"])
|
||
def merge_upload():
|
||
"""
|
||
合并已上传的文件分块
|
||
"""
|
||
data = request.json
|
||
if not data:
|
||
return jsonify({"code": 400, "message": "请求数据为空", "data": None}), 400
|
||
|
||
upload_id = data.get("uploadId")
|
||
file_name = data.get("fileName")
|
||
total_chunks = data.get("totalChunks")
|
||
parent_id = data.get("parentId")
|
||
|
||
if not all([upload_id, file_name, total_chunks]):
|
||
return jsonify({"code": 400, "message": "缺少必要参数", "data": None}), 400
|
||
|
||
result = merge_chunks(upload_id, file_name, total_chunks, parent_id)
|
||
|
||
# 检查结果中是否有错误信息
|
||
if result.get("code", 0) != 0:
|
||
# 如果有错误,返回相应的HTTP状态码
|
||
return jsonify(result), result.get("code", 500)
|
||
|
||
return jsonify(result)
|