DHCP
DHCP
Published on 2026-07-14 / 6 Visits
1
0

匿名聊天室项目介绍

从零搭建一个前后端分离的匿名聊天室:Vue 2 + Spring Boot + WebSocket

在很多 Web 项目中,聊天功能看起来并不复杂:用户输入一段文字,点击发送,其他人立刻看到消息。但真正开始实现后,会发现它同时涉及实时通信、连接管理、身份认证、消息持久化、图片压缩、异常反馈和生产环境反向代理等多个问题。

本文介绍一个已经可以完整运行和部署的匿名聊天室项目。项目采用 Vue 2 和 Spring Boot 前后端分离架构,使用 STOMP over WebSocket 传输实时消息,并通过 MySQL 保存聊天记录。除了文字聊天,它还支持图片发送、登录注册、在线人数统计、连接状态提示以及移动端适配。

一、项目功能

当前聊天室包含以下主要功能:

  • 支持匿名身份直接进入聊天室,也支持注册和登录。
  • 使用 WebSocket 实时收发消息,无需轮询接口。
  • 使用 MySQL 持久化聊天记录,刷新页面后仍可加载历史消息。
  • 支持粘贴或拖拽图片,并在浏览器中自动压缩后发送。
  • 支持大图片多轮压缩,在控制消息体积的同时尽量减少画质损失。
  • 显示已连接、连接中、重连中、连接失败等连接状态。
  • 统计当前在线用户,并在断线、退出和重新连接时更新。
  • 输入框支持 Enter 发送、Shift + Enter 换行。
  • 对桌面端和移动端进行响应式适配。

二、技术栈

模块技术
前端框架Vue 2.6、Vue Router
UI 组件Element UI
HTTP 请求Axios
实时通信WebSocket、STOMP.js
后端框架Spring Boot 2.7
安全与认证Spring Security、JWT、BCrypt
数据访问Spring Data JPA
数据库MySQL 8
构建工具npm、Vue CLI、Maven
生产部署JAR、Nginx、HTTPS 反向代理

三、整体架构

项目将普通 HTTP 请求和实时消息分为两条链路:登录、注册、历史消息和在线用户查询使用 REST API;聊天消息和用户状态变化使用 WebSocket。

REST: /api
WebSocket: /ws
HTTP
WebSocket Upgrade
/topic/public
浏览器 / Vue 前端
Nginx 反向代理
Spring Boot 后端
认证、消息校验和在线状态服务
MySQL

生产环境使用同源路径有两个好处:前端不需要把后端地址写死在构建产物中,同时可以减少 HTTPS 页面连接非加密 WebSocket 时产生的混合内容问题。

四、前端实时通信实现

前端通过 @stomp/stompjs 创建客户端,连接 /ws 后订阅两个主题:

  • /topic/public:接收公开聊天消息。
  • /topic/users:接收用户加入、退出和断线事件。

用户发送消息时,前端向 /app/chat.sendMessage 发布 STOMP 消息。后端完成校验和入库后,再将结果广播给所有在线客户端。

连接并不总是稳定,因此页面不能只维护一个简单的“已连接”布尔值。项目将连接状态细分为连接中、已连接、正在重连、连接失败和已断开,并开启心跳和自动重连。这样用户能够明确知道当前消息是否具备发送条件,而不是点击按钮后没有任何反馈。

五、图片发送与压缩

图片消息没有单独走文件上传接口,而是在浏览器中转为 Base64 Data URL,随后作为一条 IMAGE 类型的聊天消息发送。这种方案实现简单,适合中小规模聊天室,但 Base64 通常会比原始二进制数据增加约三分之一的体积,因此必须限制消息大小。

图片处理流程如下:

  1. 检查文件类型、文件大小和待发送图片数量。
  2. 使用 FileReader 读取图片,并通过 Image 对象解码。
  3. 将最长边限制在合理范围内,绘制到 Canvas。
  4. 尝试多个 JPEG 质量等级。
  5. 如果仍然超过目标大小,则按比例缩小尺寸并重新编码。
  6. 达到消息大小限制后加入预览列表,等待用户发送。

项目还为图片处理增加了加载状态和错误提示。压缩期间发送按钮会被暂时禁用,避免图片尚未处理完成时发送空消息或重复点击。

一次真实的大图片故障

开发过程中出现过一个很典型的问题:小图片可以发送,但一张 1.6MB 的图片点击发送后没有反应,数据库中也没有记录。

最初看起来像前端压缩失败,但继续检查后发现,线上旧数据库的 messages.content 仍然是 MySQL 的 TEXT 类型,最大只能保存 65,535 字节。大图即使经过前端压缩,生成的 Base64 仍可能超过这个上限,因此 MySQL 在严格模式下直接拒绝插入。

最终从两个层面解决了问题:

  • 前端采用多轮质量调整和尺寸缩放,将单条图片消息控制在 WebSocket 和后端允许的范围内。
  • 数据库将消息内容字段扩展为 MEDIUMTEXT,避免旧的 64KiB 上限。
ALTER TABLE messages MODIFY content MEDIUMTEXT NOT NULL;

这个问题说明,处理大消息时不能只关注 WebSocket 帧大小,还要同时检查前端编码后的体积、后端字段校验以及数据库列容量。任何一层限制不一致,都会导致消息在链路中途失败。

六、后端消息处理

后端使用 Spring WebSocket 提供 STOMP 端点,并配置应用前缀 /app 和广播前缀 /topic。聊天消息到达控制器后,会交给服务层完成以下工作:

  • 规范化消息类型,只接受 CHATIMAGE
  • 校验用户名和消息内容长度。
  • 验证图片消息是否为合法的图片 Data URL。
  • 使用服务端时间生成消息时间戳。
  • 将消息写入 MySQL。
  • 入库成功后向所有客户端广播带数据库 ID 的消息。

服务端同时限制 WebSocket 文本消息缓冲区和应用消息大小。前后端共享清晰的大小边界,可以避免超大消息耗尽内存或造成连接异常。

七、登录与匿名身份

项目允许用户不登录直接聊天,也支持注册用户通过 JWT 建立身份。密码使用 BCrypt 哈希保存,后端不会存储明文密码。

登录后,前端在建立 STOMP 连接时通过连接头携带 JWT。后端拦截 STOMP CONNECT 帧,验证令牌并把认证用户绑定到当前 WebSocket 会话。这样即使客户端尝试修改消息中的用户名,后端仍可以优先使用认证身份,减少身份伪造风险。

匿名用户没有 JWT 时仍然可以建立连接,由后端按照消息中的匿名名称维护会话状态。

八、在线用户统计

在线人数看似只是一个数字,实际需要处理多种离开方式:

  • 用户主动退出登录。
  • 用户关闭页面或刷新页面。
  • 网络中断导致 WebSocket 被动断开。
  • 客户端自动重连并创建新会话。

后端按照 WebSocket Session ID 保存在线用户。当 Spring 收到 SessionDisconnectEvent 时,会移除对应会话并广播用户变化事件。前端收到事件后重新获取在线用户列表,从而减少只依赖浏览器卸载事件造成的统计误差。

九、生产环境部署

后端通过 Maven 打包为可执行 JAR,可以直接运行:

java -jar vue-chat-backend.jar

数据库地址、用户名、密码、JWT 密钥和跨域白名单通过环境变量提供,不应硬编码到源码或提交到版本库。

前端执行生产构建:

npm run build

构建后的 dist 目录由 Nginx 提供静态资源,并将 /api/ws 转发到后端。其中 WebSocket 代理必须保留 Upgrade 请求头:

location /ws {
    proxy_pass http://backend-server;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 600s;
}

十、本地运行

准备好 Node.js、Java 8、Maven 和 MySQL 后,可以按以下顺序启动:

# 启动后端
cd vuech2/vue-chat-backend
mvn spring-boot:run

# 启动前端
cd vuech1
npm install
npm run serve

开发环境中,Vue CLI 会把 /api/ws 代理到本地后端,因此前端代码不需要区分接口域名。

十一、还可以继续优化什么

当前实现适合个人项目和中小规模聊天室。如果继续迭代,可以考虑以下方向:

  1. 将图片上传到对象存储,聊天消息只保存图片 URL,减少数据库和 WebSocket 压力。
  2. 引入 Flyway 或 Liquibase 管理数据库版本,避免代码实体已经变化而旧表结构没有同步。
  3. 为消息增加客户端消息 ID、发送中、成功和失败状态,实现可靠的发送确认和重试。
  4. 使用 Redis 保存在线会话和发布订阅状态,为后端多实例部署做准备。
  5. 增加敏感词过滤、消息撤回、房间管理和消息分页加载。
  6. 补充 WebSocket 集成测试、前端交互测试以及生产环境监控告警。
  7. 逐步升级前端工具链,减少旧版本依赖带来的维护成本。

十二、总结

这个匿名聊天室项目覆盖了一个实时 Web 应用从开发到上线的完整链路:Vue 负责交互和图片处理,STOMP/WebSocket 负责实时通信,Spring Boot 负责认证、校验和连接管理,MySQL 负责消息持久化,Nginx 则统一承载静态资源与反向代理。

项目中最有价值的部分并不只是“消息能够实时显示”,而是对异常情况的处理:断线后如何恢复、用户离开后如何更新在线状态、大图片如何跨越前端、WebSocket、后端和数据库的多层限制。这些问题也是实时 Web 项目从演示版本走向可用版本时必须解决的工程细节。

项目地址

项目地址:https://chat.145959.xyz/
如果存在BUG、需要优化的地方或有好的想法欢迎到本文章下留言
更新日志见瞬间:https://www.145959.xyz/moments


Comment