尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
FastAPI+Vue3全栈实战:电子相册管理系统开发
基于 Python 的电子相册管理系统FastAPI Vue3是一个很典型的毕业设计或课程设计选题。表面需求只是“用户注册登录、创建相册、上传照片、查看照片”但真正写起来会同时牵扯文件上传、JWT 登录鉴权、数据表外键关系、前端 axios 封装、跨域配置和静态图片访问这一整条链路。很多同学直接把前端和后端代码堆在一起结果卡在最常见的几个地方后端接口在 Swagger 文档里能调用前端却 401图片上传成功页面上却显示不出来相册删除后照片文件还在磁盘上。这篇文章以一个可运行的网络相册管理系统为例子按照“功能边界 - 数据模型 - FastAPI 后端接口 - Vue3 前端页面 - 前后端联调 - 常见问题排查 - 生产化建议”的顺序推进。整个过程会给出可以直接用于本地的表结构、接口代码、前端请求封装和验证步骤。代码不是完整项目的每一个文件都贴出来但核心链路都会覆盖到读完能对照着补出属于自己的完整项目。1. 先拆需求这个相册系统到底要做什么1.1 一个完整的相册操作闭环网络相册系统不能只理解成“上传图片”。从用户视角看完整闭环应该是这样注册账号并登录登录成功后拿到 token。进入个人相册管理页创建一个相册例如“周末出行”。进入相册后选择本地图片上传上传完成后相册内出现缩略图或原图。点击图片可以预览删除不要的照片。退出相册管理页后再次访问受保护接口时要能识别当前用户。这个闭环决定了后端不能只写一组简单的增删改查接口。用户 A 创建相册后用户 B 不应该通过手工修改请求参数看到 A 的数据。所以每一个相册、照片接口都必须携带当前登录用户的身份并且在后端通过 user_id 做过滤。这里要注意前端把“删除按钮隐藏”并不算权限控制。用户只要拿到接口地址用浏览器或工具直接发送请求后端就会收到一个合法的用户 ID 和操作目标。所以权限校验必须放在后端不能只靠前端页面做限制。1.2 数据表拆分用户、相册、照片怎样关联相册系统最少有两种备选设计。一种是只用一张 photos 表每条记录里放 user_id用户登录后直接按 user_id 查询所有照片。另一种是拆成 users、albums、photos 三张表用外键建立层级关系。推荐后者。原因很直接照片一旦多起来需要按相册维度管理、删除、批量迁移或做封面。如果没有相册表未来加“把照片移动到另一个分类”这类功能时只能在照片表里加一个 category 字段分类和用户的关系会变得非常混乱。三张表的关系是一个用户拥有多个相册。一个相册属于一个用户。一个相册下有多张照片。一张照片属于一个相册也间接属于某个用户。对应的 SQLite 建表语句可以设计成下面这样。下面的字段能覆盖课程设计的大部分需求。CREATE TABLE users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password_hash VARCHAR(255) NOT NULL, nickname VARCHAR(50), created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE albums ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, name VARCHAR(100) NOT NULL, description TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE TABLE photos ( id INTEGER PRIMARY KEY AUTOINCREMENT, album_id INTEGER NOT NULL, user_id INTEGER NOT NULL, file_name VARCHAR(255) NOT NULL, stored_name VARCHAR(255) NOT NULL, url_path VARCHAR(255) NOT NULL, content_type VARCHAR(50), size_bytes INTEGER, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (album_id) REFERENCES albums(id) ON DELETE CASCADE, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE );这里有几个字段值得单独说明字段作用说明username password_hash用户身份不能保存明文密码哈希后保存user_id数据归属所有查询和删除都要按这个字段过滤album_id照片所属相册照片列表总是先定位某个相册file_name原始文件名展示给用户看的名称stored_name磁盘存储文件名实际保存成 uuid避免文件名冲突url_path访问路径数据库只存相对路径域名由前端拼接photos 表里冗余保存了 user_id这个设计看起来会重复因为通过 album_id 已经可以关联到用户。冗余的好处是当需要查询“当前用户全部照片”时不需要先查出用户全部相册 ID再写 IN 查询另一个作用是防止在接口传参时只校验了相册 ID却没有校验相册归属。实际上接口在操作照片前一定会校验“照片的 user_id 是否等于当前用户”。1.3 功能边界和演示范围一个适合课程设计的范围可以收敛为用户模块注册、登录、退出登录前端清 token。相册模块创建相册、查询当前用户自己的相册列表、删除自己的相册。照片模块向指定相册上传照片、查询相册下照片列表、删除照片。图片访问后端以静态目录方式提供/uploads/图片名访问。暂不做的内容例如云对象存储、图片加水印、多用户共享相册、朋友圈式时间线等可以作为后续扩展写在论文或项目文档里反而更能体现你对工程边界的理解。2. 后端环境准备与 FastAPI 项目骨架2.1 Python 环境和依赖版本建议使用 Python 3.10 或更高版本因为新版本对类型标注和异步支持都更好。数据库先使用 SQLite不需要额外安装数据库服务适合课程设计和本地演示。创建虚拟环境并安装依赖mkdir photo-system cd photo-system python -m venv venv # Windows venv\Scripts\activate # macOS / Linux source venv/bin/activate pip install fastapi uvicorn[standard] sqlalchemy pydantic python-multipart PyJWT python-dotenv这里依赖选择有几个工程原因python-multipart是处理UploadFile和表单登录请求的必要依赖漏掉它会在上传图片或表单登录时收到 500 或 422 错误。密码哈希示例使用 Python 标准库hashlib实现 PBKDF2不额外引入bcrypt或passlib避免这两个库在部分 Python 版本上因为 OpenSSL 兼容问题产生的报错。JWT 使用PyJWT字段名和异常类型比较直观。如果原始项目描述里没有明确指定数据库这里采用 SQLite 是在降低搭建成本。如果后续要接 MySQL只需要改数据库连接字符串并把 SQLAlchemy 模型里的类型按 MySQL 要求微调。2.2 后端目录结构一个干净的目录结构能减少路由导入时踩坑。建议按下面结构组织后端backend ├── app │ ├── __init__.py │ ├── config.py │ ├── database.py │ ├── models.py │ ├── schemas.py │ ├── security.py │ ├── main.py │ └── routers │ ├── __init__.py │ ├── auth.py │ ├── albums.py │ └── photos.py ├── uploads ├── requirements.txt └── .envuploads目录用于存放上传图片。FastAPI 启动时会将这个目录挂载成静态资源目录相当于把磁盘目录暴露成 HTTP 可访问路径。这样做适合课程设计和本地演示。2.3 database.py 和 models.py 的实现database.py负责创建 SQLAlchemy 引擎、Session 和 Base 类。SQLite 连接需要加上check_same_threadFalse否则 FastAPI 在多个线程中访问同一个 SQLite 连接时会报错。# app/database.py from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker, declarative_base SQLALCHEMY_DATABASE_URL sqlite:///./photo_system.db engine create_engine( SQLALCHEMY_DATABASE_URL, connect_args{check_same_thread: False} ) SessionLocal sessionmaker(autocommitFalse, autoflushFalse, bindengine) Base declarative_base() def get_db(): db SessionLocal() try: yield db finally: db.close()models.py使用 SQLAlchemy 的 Column 风格定义数据模型。虽然有更新一点的 Mapped 风格但 Column 风格在课程设计代码里更容易被阅读。# app/models.py from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey, func from sqlalchemy.orm import relationship from .database import Base class User(Base): __tablename__ users id Column(Integer, primary_keyTrue, indexTrue) username Column(String(50), uniqueTrue, indexTrue, nullableFalse) password_hash Column(String(255), nullableFalse) nickname Column(String(50)) created_at Column(DateTime, server_defaultfunc.now()) albums relationship(Album, back_populatesowner, cascadeall, delete-orphan) class Album(Base): __tablename__ albums id Column(Integer, primary_keyTrue, indexTrue) user_id Column(Integer, ForeignKey(users.id), nullableFalse, indexTrue) name Column(String(100), nullableFalse) description Column(Text) created_at Column(DateTime, server_defaultfunc.now()) owner relationship(User, back_populatesalbums) photos relationship(Photo, back_populatesalbum, cascadeall, delete-orphan) class Photo(Base): __tablename__ photos id Column(Integer, primary_keyTrue, indexTrue) album_id Column(Integer, ForeignKey(albums.id), nullableFalse, indexTrue) user_id Column(Integer, ForeignKey(users.id), nullableFalse, indexTrue) file_name Column(String(255), nullableFalse) stored_name Column(String(255), nullableFalse) url_path Column(String(255), nullableFalse) content_type Column(String(50)) size_bytes Column(Integer) created_at Column(DateTime, server_defaultfunc.now()) album relationship(Album, back_populatesphotos)relationship里的cascadeall, delete-orphan实现 ORM 层面的级联删除。删除用户时他名下的相册能自动删除删除相册时相册下的照片数据库记录也会被删除。这个功能在代码里很顺手但磁盘上的图片文件不会自动删除需要业务代码单独处理后面会讲到。2.4 启动入口和静态目录挂载main.py是整个 FastAPI 应用的入口。需要完成四件事装配 CORS 中间件、注册路由、创建数据库表、挂载 uploads 静态目录。# app/main.py from pathlib import Path from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from .database import Base, engine from . import models # noqa: F401 from .config import UPLOAD_DIR from .routers import auth, albums, photos # 建表课程设计阶段够用 Base.metadata.create_all(bindengine) # 确保图片目录存在 UPLOAD_DIR.mkdir(parentsTrue, exist_okTrue) app FastAPI(title网络相册系统 API) # 本地联调开放 Vue3 开发服务器端口 app.add_middleware( CORSMiddleware, allow_origins[http://localhost:5173, http://127.0.0.1:5173], allow_credentialsTrue, allow_methods[*], allow_headers[*], ) app.include_router(auth.router) app.include_router(albums.router) app.include_router(photos.router) # 将 uploads 目录暴露为 http://127.0.0.1:8000/uploads/xxx.jpg app.mount(/uploads, StaticFiles(directoryUPLOAD_DIR), nameuploads)Base.metadata.create_all只在表不存在时创建表不会像迁移工具那样识别已有表的字段变化。修改模型字段后如果表已经存在可能需要删除本地 db 文件重新生成。这个能力只适合开发阶段生产环境应该使用 Alembic。在config.py中统一管理配置避免多个文件各自维护路径。# app/config.py from pathlib import Path BASE_DIR Path(__file__).resolve().parent.parent UPLOAD_DIR BASE_DIR / uploads SECRET_KEY change-this-in-production ALGORITHM HS256 ACCESS_TOKEN_EXPIRE_MINUTES 60 * 24 ALLOWED_IMAGE_EXTENSIONS {jpg, jpeg, png, gif, webp} MAX_UPLOAD_SIZE 10 * 1024 * 10243. FastAPI 关键接口认证、相册、照片上传与权限控制3.1 密码哈希与 JWT 工具函数密码不能明文存进数据库。示例使用标准库实现 PBKDF2 哈希每个用户生成随机盐再用固定迭代次数生成摘要。取盐和哈希值一起存进数据库。验证时从数据库取出原始字符串拆出盐后重新计算最后用hmac.compare_digest做常量时间比较。# app/security.py import hashlib import hmac import os from datetime import datetime, timedelta, timezone import jwt from .
RELATED

相关推荐

全开源微教育系统实战:从源码解析到营销模块与数据看板二次开发

全开源微教育系统实战:从源码解析到营销模块与数据看板二次开发

简介:这是一套面向教育行业数字化转型的全开源微信小程序源码,适用于在线教育机构、知识付费平台及教培从业者快速搭建具备营销与数据分析能力的轻量级教学服务平台。资源基于微教育3.15.22全能版深度定制,集成营销模块(如拼团、分…

📅 2026/10/5 17:53:25
Prophet源码级拆解:趋势、季节性与评估的完整预测链路

Prophet源码级拆解:趋势、季节性与评估的完整预测链路

读别人源码这件事,很像拆一个瑞士手表:表面上看就几个指针在转,把后盖一打开,里面大大小小的齿轮、发条、擒纵叉全部咬合在一起。Prophet 就是这样一个适合“拆开来玩”的时间序列预测项目。网上教程大多是调包、看拟合曲线&#…

📅 2026/9/19 13:19:26
基于YOLOv8与RAG的智能垃圾分类系统:图像识别与问答技术融合实践

基于YOLOv8与RAG的智能垃圾分类系统:图像识别与问答技术融合实践

简介:本资源是一套面向高校人工智能方向毕业设计、课程设计及期末大作业的完整方案,聚焦垃圾分类场景下的图像识别与智能问答系统实现。方案融合深度学习(CNN图像分类)、自然语言处理(NLP问答解析)与小程序…

📅 2026/10/6 0:13:58
MORE NEWS

更多资讯

📰

SpeContext技术解析:大模型推理24倍加速实战

1. 突破性技术解析:SpeContext如何实现24倍推理加速上海交大团队近期发布的SpeContext技术,从根本上重构了大模型推理的计算范式。其核心创新点在于动态上下文感知计算机制,通过以下三个关键技术实现突破:计算图动态分割技术&…

📰

苹果照片数据集配VOC2007标注:Python处理与训练避坑指南

简介:这份资源面向目标检测初学者与需要苹果识别实战数据的开发者,提供一套可直接用于YOLOv3训练的苹果图像数据集及配套Python处理代码,帮助解决自建数据集时标注、增强与格式转换的繁琐问题。压缩包共约2000个文件,以1648张jpg图…

📰

矩阵行空间与列空间:概念解析与工程应用

1. 行空间与列空间的概念解析行空间和列空间是线性代数中两个核心概念,它们揭示了矩阵内在的结构特性。我第一次真正理解这两个概念是在研究生阶段的数值分析课上,当时教授用了一个生动的比喻:把矩阵看作是一个数据转换器,行空间决…

📰

AI代码审核:提升开发效率与代码质量的关键技术

1. 项目概述:当AI成为代码审核员去年团队引入AI代码审核工具后,我们的代码合并周期从平均3天缩短到6小时。这个转变让我意识到,AI正在彻底改变传统代码审核的工作方式。不同于人类审核者需要逐行检查,AI工具能在提交瞬间完成基础检…

📰

C#与HALCON联合开发机器视觉检测系统:架构、模块与验收实践

简介:围绕C#与HALCON构建的机器视觉检测系统,是一套面向机器视觉初学者与中高级开发者的完整工程源码,聚焦工件表面质量检测场景。系统按图像采集、预处理、特征提取、决策反馈和图形界面等模块组织,展示相机取图、去噪增强、形状…

📰

雷达基数据解析与显示:从RadarYHM到PPI/CAPPI的完整流程

简介:这是一份面向雷达数据处理与可视化开发者的C工程资源包,聚焦基数据解析与雷达显示两大环节。压缩包内共80个文件,以头文件和源文件为主,涵盖VC工程配置、可执行文件及图标位图等资源,整体仅7.83MB。该资源已有344…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬