Python3 Flask集成FancyFileUpload文件上传对接GCS与PostgreSQL
FancyFileUpload 与 Flask 集成实现方案
FancyFileUpload 底层基于 blueimp jQuery File Upload 封装,默认选完文件即自动向配置的url发起异步POST请求,不会触发外层表单的默认提交,原代码骨架存在路由方法缺失、函数重名等问题,以下是分步骤实现逻辑:
问题1:/upload路由接收上传文件的实现
- FancyFileUpload提交的文件字段名和input的
name属性一致,为files,可以通过Flask的request.files对象直接获取 - 框架不会自动将文件保存到本地磁盘,不手动调用
save()方法时,文件仅存在于请求内存中,请求结束后自动回收,无默认落盘路径 - 接收文件的路由必须显式允许POST方法,否则会返回405错误
代码示例:
# 补全缺失的导入模块 from flask import Flask, render_template, request, jsonify, redirect, url_for import uuid import os app = Flask(__name__) # 配置最大上传大小和前端限制对齐,此处为1MB app.config['MAX_CONTENT_LENGTH'] = 1 * 1024 * 1024 @app.route('/upload', methods=['POST']) def handle_upload(): # 从请求中提取上传的文件对象 uploaded_file = request.files.get('files') if not uploaded_file: return jsonify({"code": 1, "msg": "未接收到有效文件"}), 400 # 校验文件格式,和前端accept规则对齐 allow_suffix = {'jpg', 'jpeg', 'png'} file_suffix = uploaded_file.filename.rsplit('.', 1)[-1].lower() if '.' in uploaded_file.filename else '' if file_suffix not in allow_suffix: return jsonify({"code": 1, "msg": "不支持的文件格式"}), 400 # 若需要临时存本地,可打开以下代码,用完务必删除临时文件 # temp_path = f"/tmp/{uuid.uuid4().hex}.{file_suffix}" # uploaded_file.save(temp_path)
问题2:转存文件到GCS并生成公网URL
不需要先把文件存到本地临时目录,可以直接把请求拿到的文件流传给GCS SDK,减少磁盘IO开销,只需调整原GCS上传函数支持流上传即可:
def upload_image_to_GCS(bucket_name, file_stream, destination_blob_name, content_type): storage_client = storage.Client() bucket = storage_client.bucket(bucket_name) blob = bucket.blob(destination_blob_name) # 直接从文件流上传到GCS,无需本地落盘 blob.upload_from_file(file_stream, content_type=content_type) # 配置公网可读权限 blob.make_public() return blob.public_url
在/upload路由中追加调用逻辑:
# 生成GCS内唯一文件名,避免重名覆盖 gcs_file_path = f"site_images/{uuid.uuid4().hex}.{file_suffix}" # 调用上传方法,直接传入请求拿到的文件对象(本身是类文件流) public_url = upload_image_to_GCS( bucket_name="替换为你的GCS存储桶名", file_stream=uploaded_file, destination_blob_name=gcs_file_path, content_type=uploaded_file.content_type )
注意:运行Flask服务的账号需要拥有GCS存储桶的对象写入、权限修改权限,否则会上传失败。
问题3:存储URL到数据库并跳转
根据业务场景二选一即可:
方案1:后端全流程闭环(推荐)
文件上传到GCS拿到公网URL后,直接在/upload路由内完成数据库写入,不需要额外走/final路由:
# 替换为你自己的PostgreSQL写入逻辑 # 若使用SQLAlchemy,示例逻辑如下: # new_img_record = ImageTable(url=public_url, origin_name=uploaded_file.filename) # db.session.add(new_img_record) # db.session.commit() uploadToDatabase(public_url) # 按插件要求返回JSON结果,前端会自动显示上传成功状态 return jsonify({ "code": 0, "msg": "上传成功", "data": {"url": public_url} })
该方案不需要单独的提交步骤,用户选完文件即完成全流程,适合单图上传即时生效的场景。
方案2:保留表单提交逻辑
如果需要用户确认后再提交入库,可以在前端把上传拿到的URL暂存到隐藏域,提交表单到/final时再入库:
- 调整前端代码,添加隐藏域和上传完成回调:
<form action="/final" method="post"> <input id="demo" type="file" name="files" accept=".jpg, .png, image/jpeg, image/png"> <!-- 新增隐藏域存上传后的URL --> <input type="hidden" name="uploaded_url" id="uploaded_url"> <button type="submit">确认提交</button> </form> <script> $('#demo').FancyFileUpload({ params : {action :'fileuploader'}, maxfilesize : 1000000, 'url' :'/upload', // 上传成功回调 done: function(e, resp) { if (resp.result.code === 0) { $('#uploaded_url').val(resp.result.data.url); } } }); </script>
- 编写
/final路由(注意原代码两个路由函数重名,必须修改函数名):
@app.route('/final', methods=['POST']) def handle_final_submit(): public_url = request.form.get('uploaded_url') if not public_url: return "请先上传文件", 400 # 写入数据库 uploadToDatabase(public_url) # 跳转回首页 return redirect(url_for('home'))
常见踩坑提示
- 原代码中
/upload和/final的处理函数都命名为upload,会导致函数覆盖、路由失效,必须修改函数名 - 生产环境建议给GCS文件路径加随机字符串,避免文件名遍历、重名覆盖问题
- 若使用临时文件方案,上传完成后务必调用
os.remove(temp_path)清理文件,避免磁盘占满
内容的提问来源于stack exchange,提问作者Fian Julio
相关产品推荐
相关产品推荐

