You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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时再入库:

  1. 调整前端代码,添加隐藏域和上传完成回调:
<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>
  1. 编写/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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 22:36:20