Python前后端分离架构中如何实现文件上传功能

最近在撸一个前后端分离的网站,上传文件的时候思路卡住了,其实就是上传用户头像。

其他的都解决,用户带 token,验证 token。但是头像这块该怎么搞?我看了网上的太多方案都不是可以用在这种前后端分离的。

初步思路是这样:用户点击上传,把文件放在某个静态目录下,通过用户 id 生成一个文件名保存在数据库里面。但是这样会有两个问题:1.图片大小,格式校验在前端做还是后端做? 2,访问静态目录下的图片,使用后端生成的 url 还是后端直接读数据库,返回文件名,让前端生成 url ?还是 nginx 访问静态目录? 3. 用户只能一次只有一个头像,之前的全部丢失。。虽然这个不是什么大问题。

求教各位大佬!


Python前后端分离架构中如何实现文件上传功能

25 回复

上传成功后把图片的服务器路径和 url 返回给前端回显,前端表单提交的时候再存数据库
1.校验前后端都要做吧
2.肯定后端生成 url 呀,前端生成 url 的话如果后端域名变了不是要改


在Python前后端分离架构中实现文件上传,关键在于前后端通过API接口进行文件数据的传输。这里以FastAPI作为后端框架,Vue.js作为前端框架为例,提供一个完整的实现方案。

后端实现 (FastAPI)

首先,安装必要的库:

pip install fastapi uvicorn python-multipart

后端核心代码 (main.py):

from fastapi import FastAPI, File, UploadFile
from fastapi.middleware.cors import CORSMiddleware
import os
import uuid

app = FastAPI()

# 处理CORS,允许前端跨域请求
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # 生产环境应指定具体的前端地址
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# 确保上传目录存在
UPLOAD_DIR = "uploads"
os.makedirs(UPLOAD_DIR, exist_ok=True)

@app.post("/upload/")
async def upload_file(file: UploadFile = File(...)):
    """
    文件上传接口
    """
    # 生成唯一文件名,避免覆盖
    file_extension = os.path.splitext(file.filename)[1]
    unique_filename = f"{uuid.uuid4()}{file_extension}"
    file_path = os.path.join(UPLOAD_DIR, unique_filename)

    # 保存文件到服务器
    try:
        contents = await file.read()
        with open(file_path, "wb") as f:
            f.write(contents)
    except Exception as e:
        return {"success": False, "message": f"文件保存失败: {str(e)}"}
    finally:
        await file.close()

    # 返回文件访问信息
    return {
        "success": True,
        "message": "文件上传成功",
        "filename": unique_filename,
        "original_filename": file.filename,
        "file_path": f"/uploads/{unique_filename}"  # 假设配置了静态文件服务
    }

# 启动服务:uvicorn main:app --reload

前端实现 (Vue.js)

前端使用axios库发送包含文件的POST请求。

核心组件代码 (FileUpload.vue):

<template>
  <div>
    <input type="file" @change="handleFileChange" ref="fileInput" />
    <button @click="uploadFile" :disabled="!selectedFile">上传</button>
    <p v-if="uploadStatus">{{ uploadStatus }}</p>
  </div>
</template>

<script>
import axios from 'axios';

export default {
  name: 'FileUpload',
  data() {
    return {
      selectedFile: null,
      uploadStatus: ''
    };
  },
  methods: {
    handleFileChange(event) {
      this.selectedFile = event.target.files[0];
    },
    async uploadFile() {
      if (!this.selectedFile) return;

      const formData = new FormData();
      formData.append('file', this.selectedFile);

      try {
        // 替换为你的后端API地址
        const response = await axios.post('http://localhost:8000/upload/', formData, {
          headers: {
            'Content-Type': 'multipart/form-data'
          }
        });

        if (response.data.success) {
          this.uploadStatus = `上传成功!文件名:${response.data.filename}`;
          // 清空文件选择
          this.$refs.fileInput.value = '';
          this.selectedFile = null;
        } else {
          this.uploadStatus = `上传失败:${response.data.message}`;
        }
      } catch (error) {
        console.error('上传出错:', error);
        this.uploadStatus = '上传失败,请检查网络或服务器状态';
      }
    }
  }
};
</script>

关键点说明

  1. 后端使用UploadFile类型接收文件,它会将文件内容存储在内存或临时文件中,适合处理大文件。
  2. 前端使用FormData对象包装文件数据,并通过multipart/form-data格式发送。
  3. CORS配置是前后端分离项目的必备项,确保前端能跨域调用后端API。
  4. 文件存储时使用uuid生成唯一文件名,防止文件名冲突和安全问题。

总结建议

核心就是后端提供接收multipart/form-data的API,前端用FormData发请求。

实在想不明白,就 base64 编码以后塞到数据库。
这个所谓上传文件的问题,不是思路太少而是方法太多了,你愿意 base64 编码放数据库也行,愿意做个文件上传接口放到 web 目录下的静态内容文件夹也行,放到各大云服务商的静态存储也可以。
你的 1,2,3 除了 1 必须在后端做验证之外,其他都没有一定的做法,要看你自己愿意做成啥样。

我们前后端分离 前端去请求文件服务器的接口 像一楼说的 把返回的 URL 放 json 提交给后端就好了

就像 2 楼说的,base64 很方便,而且头像这种不需要精度很高的图片,又简单又快。

先上把上传文件标记为临时文件,当要保存这条数据的时候再把这个文件保存为永久文件,定期清理临时文件

文件存储一般是单独的文件 server,可以用云或者自己建一个

上传一般要带 token 签名,由应用生成签名,下发给前端,前端执行 upload,文件 server 校验签名

文件 server 应该有临时目录和永久目录

前端成功上传,返回文件 uuid,提交叫数据库保存的时候,应用要 call 一次文件 server 接口,文件永久保存

文件校验在前端,文件 server 不做任何格式,大小校验,只验证签名

虽然比较复杂,但是推荐这样做,方便平行扩容,备份,容灾

简单省事就放应用静态目录

后端提供统一的文件上传接口,返回 url 给前端 ,前端再提交后端啊.

说下我们现在的文件处理方式:
1. 有一个统一的文件存储服务供所有业务使用,所有业务的文件存储都依赖该文件服务进行保存。文件服务的具体存储依赖 oss。

2. 统一文件上传处理逻辑
1. 前端文件上传到统一的文件上传接口
2. 文件上传接口接收上传的文件,记录文件缓存,同时给前端返回文件 id、文件 url
3. 前端调用业务接口时带上文件 id,如保存头像接口
4. 业务接口会通过文件 id 调用文件存储服务做持久化存储,并将文件 id 记录到 user 表中
5. 前端请求用户头像的时候后端将记录的头像文件 id 生成 url 返回给前端。

如果只是简单的后台上传不提供给用户

给客户端分配 token, 并提供上传 ip 端口
服务点用这个 token 信息启动一个 websocket 进程 传完关闭进程

找个云存储,有前端的 SDK,使用 SDK 直接传云存储,返回 url,然后保存表单的时候访问服务器。

业务量小的话,可以使用七牛云的免费图片存储(后端调用接口)。然后数据库保存 url,将 url 返回给前端。对于图片限制,需要前后端都做,不过七牛云可以制定图片,解决了你后端再限制的麻烦。

用 base64 怎么做 CDN 和缓存? 总不能缓存 user json 缓存十年吧

數據庫存鏈接地址,這種實際直接可以用 lua+niginx 寫個插件 直接存 redis 啥的搞定。

这和前后端分离有啥关系?

用户上传文件,后端放到静态文件里( CDN 也可以),然后把 url 写数据库。
1. 前后都要做校验。
2. 多个头像这个看具体需求,反正就是一个保存多个 URL 的事。

前端校验上传图片,后端将图片放到 oss 上,将路径存到数据库,前端显示的时候后端生成 url 传给前端。
你也可以将文件放到你的项目目录下。

我觉得你对前后端分离有误解

base64 ? 图片大的话 确定那么多字符能存到表里? 应该前端用 FormData 上传 , 后端获取解析 存对象存储里面, 把路径名称存数据库才对吧···

为什么上面说的都没有看出来什么呢…
我想楼主可能是因为公司的项目是纯前后端分离,且 Node 本身负责了前端的渲染,造成了上传文件的时候,可以直接在前台上传到服务器上,也可以通过 Node 使用 pipe 流到后台,由后台的接口上传文件。
这个地方因为前后端完全分离,也困扰了我们很久,最后我们认为由 Node pipe 流到后台,虽然这样符合 Node 前台服务器作为转发中间层的定义,但是后台再次上传实在是太浪费资源。上传文件最终完全由前台来做了,只是上传完成后后的文件名和路径等信息由前台转发给后台,让后台存储这些上传后的资源信息

base64 放数据库,are you ok ???

url 不就是一个特别一点的字符串,跟 user 的 name 字段有区别吗?

图片上传,保存图片的 url 就可以啦
本地可以存相对地址,云储存需要多一个域名(/user/avatar/1.jpg ) vs ( http://domain.com/image/1.jpg)

图片上传,上传到本地,上传到七牛等云存储都可以

数据校验最好是前后段都要做啦,前端文件改个后缀不就能绕过了嘛(前端校验为了体验,后端校验为了安全)

实在不懂这个跟前后端分离有什么关系

用户头像什么的不都是该放在 CDN 上吗? 这些一般都是上传到 CDN 上,然后 callback 回来后,在 call 后端的 api,后端 api 一般就存个 url

docker 一个 fastdfs 系统,在 storage 上搭个 nginx 提共静态文件服务,可以试试

回到顶部