从零搭建文件上传系统:FastAPI 后端 + Streamlit 前端

【学习记录】从零搭建文件上传系统:FastAPI 后端 + Streamlit 前端

在 Web 应用中,让用户上传文件并安全存储到服务器是一个常见需求。本文将带你从零开始,使用 FastAPI (高性能异步 Python 后端)和 Streamlit(纯 Python 前端框架)构建一个完整的文件上传与存储系统。全程只需 Python 代码,无需 HTML/CSS/JavaScript,非常适合快速原型开发。


📌 技术栈简介

组件 作用 特点
FastAPI 后端 API 服务 高性能、自动生成文档、原生异步支持
Streamlit 前端交互界面 纯 Python、无需前端知识、快速迭代
Uvicorn ASGI 服务器 运行 FastAPI 应用
Requests HTTP 客户端 前端调用后端 API

🗂️ 项目结构

复制代码
file_upload_system/
├── backend.py          # FastAPI 后端
├── frontend.py         # Streamlit 前端
├── uploaded_files/     # 文件存储目录(自动创建)
└── requirements.txt    # 依赖清单

🔧 环境准备

确保 Python ≥ 3.8,安装依赖:

bash 复制代码
pip install fastapi uvicorn streamlit requests python-multipart

或创建 requirements.txt 并一键安装:

txt 复制代码
fastapi
uvicorn
streamlit
requests
python-multipart
bash 复制代码
pip install -r requirements.txt

🖥️ 后端实现(FastAPI)

创建 backend.py,实现:

  • 接收前端上传的文件
  • 检查文件大小(限制 50 MB)
  • 为文件生成唯一名称(使用 UUID,避免重名覆盖)
  • 保存到本地目录 ./uploaded_files
  • 返回上传成功的元信息
python 复制代码
import os
import shutil
from fastapi import FastAPI, File, UploadFile, HTTPException
from fastapi.responses import JSONResponse
from fastapi.middleware.cors import CORSMiddleware
import uuid

app = FastAPI(title="文档上传服务")

# 跨域配置(Streamlit 默认 8501 端口,FastAPI 8000 端口)
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],          # 开发环境允许所有源,生产环境需限制
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

UPLOAD_DIR = "./uploaded_files"
os.makedirs(UPLOAD_DIR, exist_ok=True)

@app.get("/")
async def root():
    return {"message": "文档上传服务运行中"}

@app.post("/upload")
async def upload_document(file: UploadFile = File(...)):
    MAX_SIZE = 50 * 1024 * 1024   # 50 MB
    # 检查文件大小
    file.file.seek(0, 2)
    size = file.file.tell()
    if size > MAX_SIZE:
        raise HTTPException(status_code=413, detail="文件大小超过 50 MB 限制")
    file.file.seek(0)

    # 生成唯一文件名,保留原始扩展名
    original_filename = file.filename
    ext = os.path.splitext(original_filename)[1]
    unique_filename = f"{uuid.uuid4().hex}{ext}"
    save_path = os.path.join(UPLOAD_DIR, unique_filename)

    # 保存文件
    try:
        with open(save_path, "wb") as buffer:
            shutil.copyfileobj(file.file, buffer)
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"文件保存失败: {str(e)}")

    return JSONResponse(content={
        "status": "success",
        "message": "文件上传成功",
        "original_filename": original_filename,
        "saved_filename": unique_filename,
        "saved_path": save_path,
        "file_size": size
    })

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

关键点说明

  • CORSMiddleware 解决跨域问题,让 Streamlit 前端能调用不同端口的后端。
  • UploadFile 类型自动处理 multipart 表单数据。
  • uuid.uuid4().hex 生成 32 位随机字符串,彻底避免文件名冲突。
  • shutil.copyfileobj 高效地将上传流写入磁盘,内存友好。
  • 文件大小检查通过 seek(0,2) 移动到末尾获取总大小,然后重置指针。

🎨 前端实现(Streamlit)

创建 frontend.py,提供:

  • 文件选择器
  • 文件信息展示
  • 上传按钮
  • 显示后端返回的结果
python 复制代码
import streamlit as st
import requests

API_URL = "http://localhost:8000/upload"

st.set_page_config(page_title="文档上传工具", page_icon="📄")
st.title("📄 文档上传到后端服务器")

uploaded_file = st.file_uploader("选择文件", type=None)   # 允许所有类型

if uploaded_file is not None:
    st.write(f"**文件名:** {uploaded_file.name}")
    st.write(f"**文件大小:** {uploaded_file.size} 字节")
    st.write(f"**文件类型:** {uploaded_file.type}")

    if st.button("上传到服务器"):
        files = {"file": (uploaded_file.name, uploaded_file.getvalue(), uploaded_file.type)}
        try:
            with st.spinner("上传中..."):
                response = requests.post(API_URL, files=files)
            if response.status_code == 200:
                data = response.json()
                st.success("✅ 上传成功!")
                st.json(data)
            else:
                st.error(f"❌ 上传失败: {response.status_code} - {response.text}")
        except Exception as e:
            st.error(f"❌ 连接后端失败: {e}")

关键点说明

  • st.file_uploader 返回 UploadedFile 对象,可直接获取文件名、大小、二进制内容。
  • requests.postfiles 参数构造符合 multipart/form-data 格式。
  • st.spinner 在等待响应时提供加载动画。
  • st.json 漂亮地显示后端返回的 JSON。

🚀 运行与测试

启动后端

在终端一执行:

bash 复制代码
python backend.py

输出:

复制代码
INFO:     Uvicorn running on http://0.0.0.0:8000

启动前端

在终端二执行:

bash 复制代码
streamlit run frontend.py

浏览器将自动打开 http://localhost:8501

测试流程

  1. 点击 Browse files 选择任意文件(例如 report.pdf)。
  2. 查看显示的文件名、大小和类型。
  3. 点击 上传到服务器
  4. 成功后会看到绿色提示和后端返回的 JSON,例如:
json 复制代码
{
  "status": "success",
  "message": "文件上传成功",
  "original_filename": "report.pdf",
  "saved_filename": "a1b2c3d4e5f6789...pdf",
  "saved_path": "./uploaded_files/a1b2c3d4e5f6789...pdf",
  "file_size": 123456
}
  1. 检查服务器上的 uploaded_files 目录,文件已保存。

🧪 验证上传是否成功

在服务器(或本地)执行:

bash 复制代码
ls -lh uploaded_files/

会看到类似 a1b2c3d4e5f6789...pdf 的文件,大小与上传一致。


🎯 总结与扩展思考

通过本文,你学会了:

  • ✅ 使用 FastAPI 编写文件上传 API(含大小限制、唯一命名)。
  • ✅ 使用 Streamlit 构建简单的上传界面(纯 Python,无需前端知识)。
  • ✅ 处理前后端跨域问题。
  • ✅ 结合 shutil.copyfileobj 高效保存文件。

可以在此基础上扩展的功能

  • 支持多文件并发上传(Streamlit 的 file_uploader 可设置 accept_multiple_files=True)。
  • 添加上传进度条(可配合 requests 的流式上传或使用 tqdm)。
  • 将文件存储到云存储(如阿里云 OSS、AWS S3、MinIO)。
  • 增加文件类型白名单(例如只允许 .pdf, .jpg, .png)。
  • 对上传的文件进行病毒扫描或内容校验。
  • 将文件元信息存入数据库(如 SQLite、PostgreSQL)。

这套基础架构可以快速集成到更大的 RAG 系统、文档管理平台或数据处理流水线中。希望本文对你有帮助,欢迎在实际项目中尝试并改进!

相关推荐
utmhikari1 小时前
【AI原生】用Vibe Coding写产品前端原型的一些经验
前端·ai·产品经理·web·web开发·ai-native·qoder
YAwu111 小时前
手写一个符合 Promise/A+ 规范的 Promise(附完整代码)
前端·javascript
暗不需求1 小时前
从路虎汽车小程序看微信小程序开发的最佳实践
前端·javascript·微信小程序
用户059540174461 小时前
我把RAG对话记忆测试从手工用例改成ChromaDB自动化评估,Bug发现率翻了4倍
前端·css
向日的葵0061 小时前
vue路由(二)
前端·javascript·vue.js·vue
姓王者1 小时前
解决QQ浏览器等魔改内核下SVG背景图颜色异常变白的问题 | 姓王者的博客
前端
ejinxian1 小时前
Angular v22 正式发布:Signal Forms、Angular Aria 和 AI 开发工具全面生产化
前端·javascript·angular.js
小小龙学IT1 小时前
Tauri:用 Web 技术构建桌面应用的新范式
前端
wuhen_n1 小时前
RAG 入门:检索增强生成核心原理
前端·人工智能·typescript·langchain·ai编程