采购件图像上传初实现
This commit is contained in:
132
inventory-backend/app/api/v1/common/upload.py
Normal file
132
inventory-backend/app/api/v1/common/upload.py
Normal file
@ -0,0 +1,132 @@
|
||||
# 文件路径: inventory-backend/app/api/v1/common/upload.py
|
||||
|
||||
import os
|
||||
import uuid
|
||||
from flask import Blueprint, request, jsonify, send_from_directory
|
||||
|
||||
# 定义蓝图
|
||||
upload_bp = Blueprint('upload', __name__)
|
||||
|
||||
|
||||
# =========================================================
|
||||
# 配置上传路径 (核心修改:确保路径绝对准确)
|
||||
# =========================================================
|
||||
# 向上寻找直到找到 inventory-backend 目录,或者默认为当前文件的上级目录的...上级
|
||||
# 这种方式比数 dirname 层级更稳健
|
||||
|
||||
def get_project_root():
|
||||
"""获取项目根目录 inventory-backend"""
|
||||
current_path = os.path.abspath(__file__)
|
||||
# 循环向上查找,直到找到名为 inventory-backend 的目录
|
||||
# 如果你的根目录名字不是 inventory-backend,请修改这里的判断逻辑
|
||||
# 或者直接使用相对路径回退 5 层: api/v1/common -> app -> inventory-backend
|
||||
base = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(current_path)))))
|
||||
return base
|
||||
|
||||
|
||||
BASE_DIR = get_project_root()
|
||||
UPLOAD_FOLDER = os.path.join(BASE_DIR, 'uploads')
|
||||
|
||||
# 允许上传的文件后缀
|
||||
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'bmp', 'webp'}
|
||||
|
||||
|
||||
def allowed_file(filename):
|
||||
return '.' in filename and \
|
||||
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
|
||||
|
||||
|
||||
def ensure_upload_folder_exists():
|
||||
if not os.path.exists(UPLOAD_FOLDER):
|
||||
try:
|
||||
os.makedirs(UPLOAD_FOLDER)
|
||||
print(f"✅ [Upload] 目录创建成功: {UPLOAD_FOLDER}")
|
||||
except Exception as e:
|
||||
print(f"❌ [Upload] 目录创建失败: {e}")
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 1. 文件上传接口
|
||||
# URL: /api/v1/common/upload (POST)
|
||||
# ------------------------------------------------------------------
|
||||
@upload_bp.route('/upload', methods=['POST'])
|
||||
def upload_file():
|
||||
ensure_upload_folder_exists()
|
||||
|
||||
if 'file' not in request.files:
|
||||
return jsonify({"code": 400, "msg": "未找到文件部分"}), 400
|
||||
|
||||
file = request.files['file']
|
||||
|
||||
if file.filename == '':
|
||||
return jsonify({"code": 400, "msg": "未选择文件"}), 400
|
||||
|
||||
if file and allowed_file(file.filename):
|
||||
try:
|
||||
ext = file.filename.rsplit('.', 1)[1].lower()
|
||||
new_filename = f"{uuid.uuid4().hex}.{ext}"
|
||||
|
||||
save_path = os.path.join(UPLOAD_FOLDER, new_filename)
|
||||
file.save(save_path)
|
||||
|
||||
print(f"💾 [Upload] 文件已保存: {save_path}")
|
||||
|
||||
# 生成访问 URL
|
||||
# 这里的路径必须与 __init__.py 中注册的 url_prefix + 路由匹配
|
||||
file_url = f"/api/v1/common/files/{new_filename}"
|
||||
|
||||
return jsonify({
|
||||
"code": 200,
|
||||
"msg": "上传成功",
|
||||
"data": {
|
||||
"url": file_url,
|
||||
"filename": new_filename
|
||||
}
|
||||
})
|
||||
except Exception as e:
|
||||
print(f"❌ [Upload] 保存异常: {e}")
|
||||
return jsonify({"code": 500, "msg": "文件保存失败"}), 500
|
||||
|
||||
return jsonify({"code": 400, "msg": "不支持的文件格式"}), 400
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 2. 静态文件访问接口 (回显)
|
||||
# URL: /api/v1/common/files/<filename>
|
||||
# ------------------------------------------------------------------
|
||||
@upload_bp.route('/files/<filename>')
|
||||
def uploaded_file(filename):
|
||||
# 打印日志帮助调试 404 问题
|
||||
full_path = os.path.join(UPLOAD_FOLDER, filename)
|
||||
if not os.path.exists(full_path):
|
||||
print(f"❌ [File Access] 文件未找到: {full_path}")
|
||||
return jsonify({"code": 404, "msg": "文件不存在"}), 404
|
||||
|
||||
return send_from_directory(UPLOAD_FOLDER, filename)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 3. 文件删除接口 (同步删除物理文件)
|
||||
# URL: /api/v1/common/files/<filename> (DELETE)
|
||||
# ------------------------------------------------------------------
|
||||
@upload_bp.route('/files/<filename>', methods=['DELETE'])
|
||||
def delete_file(filename):
|
||||
try:
|
||||
# 安全处理文件名
|
||||
safe_filename = os.path.basename(filename)
|
||||
file_path = os.path.join(UPLOAD_FOLDER, safe_filename)
|
||||
|
||||
print(f"🗑️ [Delete] 尝试删除文件: {file_path}")
|
||||
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
print(f"✅ [Delete] 文件删除成功")
|
||||
return jsonify({"code": 200, "msg": "文件已删除"})
|
||||
else:
|
||||
print(f"⚠️ [Delete] 文件不存在,无需删除")
|
||||
# 即使文件不存在也返回成功,保证前端流程继续
|
||||
return jsonify({"code": 200, "msg": "文件不存在或已删除"})
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ [Delete] 删除异常: {e}")
|
||||
return jsonify({"code": 500, "msg": f"删除失败: {str(e)}"}), 500
|
||||
Reference in New Issue
Block a user