✨ 使用 Flask 实现头像文件上传与加载功能

文章目录

      • [🧱 技术栈](#🧱 技术栈)
      • [🗂️ 项目结构与配置](#🗂️ 项目结构与配置)
      • [🔐 用户身份校验逻辑](#🔐 用户身份校验逻辑)
      • [📤 头像上传接口:`/file/avatar/upload`](#📤 头像上传接口:/file/avatar/upload)
      • [📥 加载头像接口:`/file/avatar/load/<filename>`](#📥 加载头像接口:/file/avatar/load/<filename>)
      • [🧪 示例请求(使用 cURL 测试)](#🧪 示例请求(使用 cURL 测试))
      • [🔒 安全性与优化建议](#🔒 安全性与优化建议)

在 Web 应用中,文件上传是一个非常常见且不可或缺的需求,尤其是在涉及用户头像、图片展示等功能时。本文将基于 Flask 框架,结合简单的用户身份验证逻辑,实现一个完整的"头像上传与加载"模块。


🧱 技术栈

我们的模块采用了一套简洁而高效的技术栈:

  • 后端框架: Flask
  • 数据库: MySQL(通过自定义的 SQL.DatabasePool 封装类访问)
  • 缓存: Flask-Caching(示例中配置为 simple 缓存)
  • 安全处理: 文件名加密、路径过滤、防止目录遍历攻击

🗂️ 项目结构与配置

首先,我们定义好上传文件的保存路径。这确保了所有头像都存储在一个集中且易于访问的位置:

python 复制代码
import os

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
# 假设 'apps/fileManage' 是一个子模块,调整 BASE_DIR 到项目根目录
BASE_DIR = BASE_DIR.split(r"\apps\fileManage")[0]
UPLOAD_FOLDER = os.path.join(BASE_DIR, 'uploads')
os.makedirs(UPLOAD_FOLDER, exist_ok=True)

这段代码保证了以下几点:

  • 上传文件将位于项目根目录下的 uploads 文件夹中。
  • 如果 uploads 文件夹不存在,它将自动创建,避免常见的系统错误。

🔐 用户身份校验逻辑

在执行任何文件操作之前,服务器会通过检查传入 Cookie 中的 token 字段来验证用户身份。这一关键步骤确保只有授权用户才能上传或访问文件。

python 复制代码
# 假设 'request' 已从 flask 导入
cookie = request.headers.get('Cookie')
# ... 额外解析以从 cookie 字符串中提取 token
token = dict1.get('token') # 假设 dict1 已填充 cookie 键值对
res = sql.executeQuery(
    "SELECT userId FROM onlineUsers JOIN users ON onlineUsers.SessionId = users.userId WHERE onlineUsers.SessionId = '{}'".format(token)
)

如果用户未登录或会话失效,服务器将返回 401 Unauthorized 错误,从而保护您的应用程序免受未经授权的访问。


📤 头像上传接口:/file/avatar/upload

POST 接口负责处理头像图片的上传。完整的流程设计注重鲁棒性和安全性:

  1. 身份验证: 系统首先解析 Cookie 并验证用户会话的合法性。
  2. 文件校验:
    • 它会验证请求中是否存在文件(file 对象)。
    • 文件名不能为空。
    • 至关重要的是,只允许图片文件扩展名(.png.jpg.jpeg.gif),防止上传潜在的恶意文件类型。
  3. 重命名处理: 为了避免命名冲突并确保每个头像都有一个唯一的标识符,文件名会使用 MD5 进行哈希处理,结合用户名和原始文件名生成一个唯一字符串,并附加原始文件扩展名。
  4. 文件保存: 经验证和重命名的文件随后安全地保存到服务器上指定的 uploads 目录中。
  5. 数据库更新: 新上传头像的路径会更新到 users 表中,将头像与相应的用户关联起来。
  6. 返回结果: 成功消息以及可访问的文件路径会返回给客户端。

核心实现代码如下:

python 复制代码
# 假设 'request' 已从 flask 导入,并且 'encrypt' 和 'sql' 已定义
file = request.files['file']
# 使用用户名和原始文件名的 MD5 哈希生成唯一文件名
newFilename = encrypt.hash_md5(username + file.filename) + '.' + file.filename.split('.')[-1]
file.save(os.path.join(UPLOAD_FOLDER, newFilename))
# 更新数据库中用户的头像路径
sql.executeUpdate("UPDATE users SET avatar = 'server:{}' WHERE userId = {}".format(newFilename, userId))

📥 加载头像接口:/file/avatar/load/<filename>

GET 接口根据唯一的头像文件名提供已上传的图片资源。它包含了多项安全和性能增强:

  • 路径过滤: 这是一项关键的安全措施,可防止用户在文件名中构造 ../ 序列,从而尝试进行目录遍历攻击并访问 uploads 目录之外的未经授权的文件。
  • 后缀验证: 只允许提供图片文件类型,防止暴露非图片文件。
  • 缓存处理: 该接口支持浏览器缓存,通过减少服务器请求显著提高频繁访问头像的性能。(更多内容请参阅"安全性与优化建议"部分。)
  • MIME 类型推断: 服务器会根据文件扩展名自动推断并设置响应的适当 Content-Type(MIME 类型)(例如,image/pngimage/jpeg),确保浏览器正确渲染图像。
python 复制代码
# 假设 'Response' 已从 flask 导入,并且 'data' 包含图像内容
mimetype = f'image/{filename.rsplit(".", 1)[-1].lower()}'
return Response(data, mimetype=mimetype)

🧪 示例请求(使用 cURL 测试)

为了帮助您测试和理解 API 交互,以下是上传和加载头像的 cURL 命令:

上传头像
bash 复制代码
curl -X POST http://yourdomain.com/file/avatar/upload \
  -H "Cookie: token=YOUR_TOKEN" \
  -F "file=@/path/to/your/avatar.png"

请将 YOUR_TOKEN 替换为有效的用户会话令牌,将 /path/to/your/avatar.png 替换为您的图片文件的实际路径。

加载头像
bash 复制代码
curl http://yourdomain.com/file/avatar/load/xxxxxx.png --output avatar.png

请将 xxxxxx.png 替换为您希望加载的头像的实际唯一文件名。--output avatar.png 标志会将下载的图像保存为 avatar.png 文件。


🔒 安全性与优化建议

虽然我们的模块提供了坚实的基础,但对于生产环境,请考虑以下额外的措施:

  • 文件名哈希: 已实现,这一关键步骤通过确保唯一文件名来防止用户互相覆盖文件。

  • 路径遍历保护: 已实现,这可以防止恶意用户访问指定上传目录之外的文件。

  • 文件大小限制: 实现服务器端检查以限制最大文件大小。这可以防止由于上传过大文件而导致的拒绝服务攻击,并节省服务器资源。

  • 健壮的错误处理: 增强文件操作的错误处理(例如,磁盘已满、权限问题),以便为用户和管理员提供更具信息量的反馈。

  • 云存储集成: 对于生产环境,特别是流量较高的场景,请考虑将头像存储卸载到专门的云存储解决方案,如阿里云 OSS七牛云Amazon S3。这可以显著提高可伸缩性、可靠性,并减轻应用程序服务器的负担。

  • 浏览器缓存以提高性能: 明确地为头像等静态资源向 Response 添加 Cache-Control 头。这允许浏览器缓存图像,从而加快后续加载速度并减少服务器负载。

    python 复制代码
    return Response(data, mimetype=mimetype, headers={'Cache-Control': 'public, max-age=86400'})

    此头信息告诉浏览器将图像缓存 24 小时(86400 秒)。

相关推荐
love530love18 小时前
LiveTalking 数字人项目 Windows 部署完全指南(EPGF 架构)
人工智能·windows·python·架构·livetalking·epgf
遇事不決洛必達18 小时前
【Python基础】GIL 锁是什么及其对爬虫的影响
爬虫·python·线程·进程·gil锁
星辰徐哥18 小时前
Spring Boot 微服务架构设计与实现
spring boot·后端·微服务
星辰徐哥18 小时前
Spring Boot 数据导入导出与报表生成
spring boot·后端·ui
明夜之约18 小时前
Spring Boot 自动装配源码
java·spring boot·后端
Leaton Lee18 小时前
Spring Boot分层架构详解:从Controller到Service再到Mapper的完整流程
java·spring boot·后端·架构
Micro麦可乐18 小时前
Spring Boot 实战:从零设计一个短链系统(含完整代码与数据库设计)
数据库·spring boot·后端·哈希算法·雪花算法·短链系统
Jinkxs18 小时前
Resilience4j- 与 Spring Boot 快速集成:自动配置与基础注解使用
java·spring boot·后端
毕设源码_郑学姐18 小时前
计算机毕业设计springboot网络相册设计与实现 基于Spring Boot框架的在线相册管理系统开发与应用 Spring Boot驱动的网络影集设计与实践
spring boot·后端·课程设计
辣机小司18 小时前
【踩坑记录:Spring Boot 配置文件读取值不一致?警惕 YAML 的“八进制陷阱”与 SnakeYAML 版本之谜】
java·spring boot·后端·yaml·踩坑记录