基于SSM+Vue+MySQL的在线视频点播系统毕设完整实现与部署

发布时间:2026/10/31 5:29:00
基于SSM+Vue+MySQL的在线视频点播系统毕设完整实现与部署

简介:基于Java、SSM、Vue与MySQL构建的在线视频点播系统,是一套面向计算机专业毕业设计与课程设计的高分项目资料包。系统涵盖用户端和后台管理端,包含视频分类检索、在线播放、评论管理、用户管理等典型模块,前后端代码完整,结合数据库脚本与论文,能帮助学习者深入理解SSM整合与Vue前端交互的实现流程。压缩包共1414个文件,以Java源码、JSP、HTML、JS、CSS及SQL脚本为主,同时包含XML配置、图片与视频素材、说明文档等,整体48.04MB,可在Tomcat与Maven环境下直接运行;项目经过严格调试,界面美观、操作简便,适合答辩演示或作为课设、实训的参考模板。已有44人学习,值得正在选题或需要可运行范例的开发者使用。

1. 在线视频点播系统:这套 SSM+Vue+MySQL 毕设源码解决的是哪类问题

这套基于 java + ssm + vue + mysql 的在线视频点播系统,是计算机专业毕业设计里非常典型的一类:Vue 负责页面与路由,SSM 处理接口和业务,MySQL 存用户、视频、评论,压缩包里还带了初始化数据库脚本和答辩用论文。它解决的核心问题不是“视频网站怎么做”,而是把 Java 后端、前端工程和数据库设计串成一条完整链路——注册登录、视频上传、分类浏览、播放统计、后台管理一站到底。适合正在赶毕设的大四学生,也适合想用完整项目练手的 Java 入门开发者。如果你已经会写单点接口,这套源码最大的价值是让你看清分层架构和前后端联调的全貌。

2. SSM+Vue+MySQL 技术选型拆解:三个组件各自扛哪一层

2.1 先分清 SSM 和 Spring Boot:pom.xml 与 web.xml 能告诉你一切

很多标题写着 ssm 的毕设源码,打开之后其实是 Spring Boot 项目。不要急着配环境,第一件事是打开 pom.xml 数一数依赖:看到 spring-webmvc、mybatis、mybatis-spring、mysql-connector-java 这一串,并且 WEB-INF 目录下躺着 web.xml,这才是教科书意义上的纯 SSM;如果看到 spring-boot-starter-web,那就是 Spring Boot 穿了件 ssm 的马甲。这两者的部署方式完全不同,后者直接 java -jar,前者要丢进 Tomcat 这类 Java 容器的 webapps 目录。判断错了,后面所有配置都会跑偏。

为什么毕业设计还坚持用经典 SSM:因为论文里能画出 DispatcherServlet、Controller、Service、Mapper 的请求流转图,能讲清楚 Spring 容器管理了什么、事务边界划在哪里。Spring Boot 把这些都藏起来了,答辩时反而不好展开。纯 SSM 的代码里你一定会见到这些注解,也就是面试题里常刷的 ssm 常用注解:@Controller 标记控制层、@Service 标记业务层、@Autowired 做依赖注入、@RequestMapping 映射 URL、@ResponseBody 把 Java 对象转 JSON、@Transactional 管事务,再加 @RequestParam 接参数。这些注解的基础,其实还是 Java 基础里 Spring 容器那套 IoC 和 AOP 概念。

以视频列表接口为例,一个典型 Controller 长这样:

@Controller @RequestMapping("/video") public class VideoController { @Autowired private VideoService videoService; @RequestMapping("/list") @ResponseBody public Result list(@RequestParam(defaultValue = "1") Integer page, @RequestParam(defaultValue = "12") Integer size) { return Result.ok(videoService.page(page, size)); } }

这里 @RequestMapping("/list") 对应前端 axios 请求的 /video/list,page 和 size 都给了默认值,前端不传参也不会报错;@ResponseBody 配合统一的 Result 包装对象,让返回结构固定成 {code, msg, data}。VideoService 里通常再套一层 @Transactional,后面讲事务时还会用到。Service 再调 VideoMapper 的接口,XML 里写 SQL,一层一层分清楚,这就是答辩时“分层架构”四个字的实物证据。

2.2 Vue 路由与 axios:一个播放请求从页面到数据库要穿过几层

前端这半边,这类项目几乎默认 Vue 2 + Element UI,路由用 vue-router。你打开 src/router 下的配置文件,会看到一堆 path、component、name 的组合,这叫 vue 动态路由的静态写法。播放页尤其典型,路径里带一个冒号参数:

const routes = [ { path: '/', redirect: '/home' }, { path: '/home', component: Home }, { path: '/video/detail/:id', component: VideoDetail, name: 'videoDetail' } ]

冒号 id 是动态段,前端跳转时写法是 this.$router.push({ name: 'videoDetail', params: { id: 1 } }),组件里用 this.$route.params.id 取出来。这就是前端分离的基础约定:路由参数只负责页面定位,真正取数据靠 axios。

一次播放请求从浏览器到数据库要穿过这样一条链路:VideoDetail.vue 的 created 钩子里用 axios 发起 GET,开发环境下前端跑在 8081,后端 SSM 跑在 8080,端口不同必然跨域。多数源码在 vue.config.js 里配了 devServer 转发规则,把 /api 开头的请求转给后端:

devServer: { port: 8081, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } }

changeOrigin 让请求头里的 Host 变成目标地址,否则后端基于域名判断的逻辑会误伤;如果 war 包名是 vod,后端带 /vod 上下文路径,target 要写全,否则一样 404。后端 Controller 用 @RequestMapping("/video/detail") 配合 @RequestParam 收 id,再一路压到 VideoMapper,最后落到 SELECT * FROM video WHERE id = ?。从 URL 到 SQL,正好五层,论文里的系统架构图就是这条线。

2.3 MySQL 表结构:像高分项目该有的五张核心表

一个能拿高分的点播系统,数据库至少要有用户、管理员、视频、分类、评论、收藏这几张表,设计得再全一点会加一张播放记录表。核心是视频表,字段直接决定你能给答辩老师讲出多少内容:

CREATE TABLE `video` ( `id` int(11) NOT NULL AUTO_INCREMENT, `title` varchar(200) NOT NULL COMMENT '视频标题', `cover` varchar(255) DEFAULT '' COMMENT '封面图路径', `url` varchar(255) NOT NULL COMMENT '播放地址,mp4或m3u8', `category_id` int(11) DEFAULT NULL COMMENT '所属分类', `play_count` int(11) DEFAULT '0' COMMENT '播放量', `status` tinyint(4) DEFAULT '0' COMMENT '0下架 1上架', `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

play_count 默认 0,对应后端判空省事的写法;status 字段做上下架,比直接 DELETE 保得住数据;create_time 用 CURRENT_TIMESTAMP 自动生成,省掉后端手动塞时间。表引擎必须是 InnoDB,因为评论、播放量这类操作涉及 mysql 事务处理。

事务在点播系统里最典型的场景是“评论 + 计数 + 更新热度”三件事一起做。只用一条 UPDATE video SET play_count = play_count + 1 WHERE id = ? 就能让播放量自增,这是原子操作,不需要先 SELECT 再 UPDATE,能避免并发时互相覆盖。你在 mysql 事务处理 的资料里看到过乐观锁、悲观锁,毕设里用不上那么重,但 @Transactional 一定要加对位置,通常放在 Service 方法上,而不是 Controller 里。

列表和热榜还依赖 mysql 排序 的功底:SELECT * FROM video WHERE status = 1 ORDER BY play_count DESC, create_time DESC LIMIT 12 OFFSET 0。给 category_id 和 play_count 建普通索引就够,别为了答辩好看给每个字段都建索引,写入会变慢,面试官追问索引代价时容易答不上来。记得 video 表的 url 和 cover 存的是相对路径,不是 base64,也不是带盘符的绝对路径,这点后面部署视频播放时非常关键。

3. 从 zip 到能跑通:环境版本搭配、数据库导入与首次启动

3.1 版本搭配:JDK、Maven、Tomcat、MySQL、Node 怎么配不打架

先把版本矛盾说清楚再动手。这类 SSM 源码对版本非常敏感,差一个大版本就报一堆莫名其妙的异常。我一般按这张表来配:

组件推荐版本说明
JDK1.8老 SSM 项目在 JDK 8 上最稳,换 17 容易遇到模块化报错
Maven3.6.x3.6 对老插件兼容性好,需要时配阿里云镜像加速
Tomcat8.5 或 9.0不要用 Tomcat 10,javax 换 jakarta 后老代码直接 ClassNotFound
MySQL5.7 或 8.0驱动版本跟着库走,8.0 必须配 8.x 驱动
Node14 或 16Vue 2 + node-sass 对 Node 版本非常敏感,18 以上常编译失败
Vue CLI4.x 或 5.x4 代配 Node 14 最省心

网上 mysql 安装教程、mysql 下载安装 一搜一大把,但毕设最常踩的坑是:装 MySQL 5.7.44 时 root 密码策略选了复杂模式,回头自己就忘;装 8.0 又遇到 caching_sha2_password 加密插件。我的习惯是安装时直接选 Legacy 加密,把密码记到笔记文件里,再用客户端连一次确认能进,再导入数据。安装配置教程里教你的图形界面步骤,到这里就够用了,不需要再动任何高级参数。

3.2 导入数据库:命令行执行 SQL 与 utf8mb4 编码检查

数据库脚本一般在压缩包的 sql 或 database 目录下,拿到手先不要用 IDE 双击乱导。命令行最可靠,三步走:

mysql -uroot -p -e "CREATE DATABASE vod DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;" mysql -uroot -p vod < db/vod.sql mysql -uroot -p -e "USE vod; SHOW TABLES;"

第一条命令建库并指定 utf8mb4,避免导入默认库后中文变问号;第二条把建表语句和数据一起导入;第三条确认 video、user、admin 这些关键表存在。如果 IDE 里表名大小写和代码对不上,查 lower_case_table_names 配置,Linux 默认区分大小写、Windows 不区分,这是跨平台部署的第一处坑。

导完检查字符集:SHOW VARIABLES LIKE 'character_set%';。如果表是 latin1,用 ALTER TABLE video CONVERT TO CHARACTER SET utf8mb4; 转一下。如果导入时报 sql_mode 错误,多半是 5.7 的 ONLY_FULL_GROUP_BY 在作怪,去查 sql_mode 并调整 MySQL 配置,而不是在连接串里乱加参数。这一步做完,数据库部分才算真正落地。

3.3 改配置启动后端:jdbc.properties、Tomcat 与上传路径

后端配置集中在 src/main/resources 下,最关键的是 jdbc.properties,有的项目叫 db.properties。这类项目典型的配置长这样:

jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/vod?useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai jdbc.username=root jdbc.password=123456

前两行是驱动和地址。useSSL=false 是开发环境的常规操作,否则 MySQL 8 会拿自签证书做 SSL 握手,日志刷一堆警告还偶发失败,这就是 mysql ssl 连接错误最常见出处;allowPublicKeyRetrieval=true 是因为 MySQL 8 默认 caching_sha2_password,第一次连接要交换公钥,不加就报 Public Key Retrieval is not allowed;serverTimezone 必须显式指定,否则 Java 拿到的日期比本地少 8 小时。后两行是账号密码,注意别提交到 Git,答辩演示前改成自己的。

然后构建部署。两种常见做法:一是 Maven 打包成 war 丢进 Tomcat 的 webapps;二是项目里配了 tomcat7-maven-plugin,直接 mvn tomcat7:run。IDE 里用 IDEA 配本地 Tomcat 最方便,能打断点,答辩演示时可控:

mvn clean package -DskipTests cp target/vod.war $TOMCAT_HOME/webapps/ $TOMCAT_HOME/bin/startup.sh

提示:启动后先看 catalina.out 日志,Java 容器部署失败九成都是依赖缺失或端口占用,日志第一屏就会告诉你答案。

启动后访问 http://localhost:8080/vod/ 看到首页或接口返回即成功。出现 ClassNotFoundException 时,先确认 IDEA 部署的 Artifacts 里有没有把 lib 目录带上,这是经典漏配。

3.4 启动 Vue 前端并验证首条链路:登录、列表、播放

前端目录一般是 frontend、web、vue 之类,确认里面有 package.json,然后执行:

npm config set registry https://registry.npmmirror.com npm install npm run dev

第一条把下载源换成国内镜像,省得卡在下载依赖;第二条装依赖,node-sass 这类包对 Node 版本非常挑,编译报错就换 Node 版本而不是硬刚;第三条把 dev server 起在 8081。打开页面先登录,初始化 SQL 里通常会种一个管理员账号,去 admin 或 user 表查明文密码或 MD5,常见是 admin/123456。登录后能看到视频列表,点进去走到播放器,这条链路通了,环境就算搭完。对 Vue 入门 阶段的同学,这一步也是理解“前端工程怎么和后端配合”的最好实例。

想要生产演示更省事,npm run build 之后把 dist 内容拷到 Tomcat webapps/vod/ 下,前端就不再依赖 8081 的 dev server。注意这里别急着刷新子路由页面,那是一个高频翻车点,第 5 章专门讲。

4. 核心播放链路落地:视频上传、m3u8 转码与 Vue 播放器接入

4.1 后端上传接口与静态资源映射:文件存哪、URL 怎么对

视频上传是点播系统区别于普通 CRUD 的地方,也是论文里“系统实现”章节的重头。后端先定义一个接收 MultipartFile 的接口:

@Controller @RequestMapping("/admin/video") public class VideoManageController { @Value("${upload.path}") private String uploadPath; @PostMapping("/upload") @ResponseBody public Result upload(@RequestParam("file") MultipartFile file, @RequestParam("title") String title) throws IOException { String ext = StringUtils.getFilenameExtension(file.getOriginalFilename()); String filename = UUID.randomUUID().toString().replace("-", "") + "." + ext; file.transferTo(new File(uploadPath + "/" + filename)); // 此处省略 insert 到 video 表的逻辑,url 存 "/upload/" + filename return Result.ok(filename); } }

UUID 重命名是为了避免中文文件名和重名覆盖;url 字段存相对路径 /upload/xxx.mp4,不是 D:\ 这种绝对路径,否则换台电脑项目就瘫。File.transferTo 是 Spring 提供的便捷方法,底层还是流拷贝。文件落盘后要让 URL 能被浏览器访问,SpringMVC 里加一段资源映射:

<mvc:resources mapping="/upload/**" location="file:/data/vod/upload/"/>

mapping 是浏览器地址里的前缀,location 是磁盘真实目录,file: 前缀表示读文件系统。location 建议放在 Tomcat 之外,比如 /data/vod/upload,这样重新部署 war 时不会把视频清掉。上传大小限制在 multipartResolver 里配,maxUploadSize 按需调,默认 2MB 根本不够传视频。

4.2 从 mp4 到 m3u8:转码命令与不转码的备选方案

毕设里如果只存 mp4 用 HTML5 video 播,能跑通但亮点不够。真正像样的方案是把 mp4 转成 m3u8 分片,这就是 vue 播放 m3u8 那类需求的技术源头。m3u8 是 HLS 协议的播放列表,里面是一串 .ts 小分片,浏览器拉一个 10 秒的分片只占少量带宽,拖动进度条也比整文件顺畅。

转码工具是 ffmpeg,一条命令足够:

ffmpeg -i input.mp4 -codec copy -hls_time 10 -hls_list_size 0 \ -hls_segment_filename "video_%03d.ts" playlist.m3u8

-codec copy 表示不重新编码,只切分,速度快且不损画质,前提是源文件已经是 H.264 + AAC;-hls_time 10 表示每 10 秒一片;-hls_list_size 0 表示保留全部分片,而不是只留最近几个;生成 playlist.m3u8 后,把 m3u8 和 ts 文件都放到 upload 目录的同一层。如果源文件编码不是 H.264,去掉 -codec copy 改用 -c:v libx264 -c:a aac,耗时变长但兼容性最好。

没有 ffmpeg 时最省事的备选是 mp4 直出,功能演示完全不受影响,只是答辩 PPT 上少了“分片传输”这个亮点。对于线上数据,常见做法是上传后异步转码而不是同步等待,因为一条长视频转码可能要几分钟,把请求堵住不现实。毕设里可以先同步转码,放在上传接口之后调用,或者用一个后台线程池处理,用 status 字段存 0/1 表示转码中/就绪,前端轮询。

4.3 前端播放器组件:vue-router 参数与 hls.js 免安装接入

前端播放器用 hls.js 最省事:在 index.html 里用 script 标签引入公共 CDN 或项目静态目录里的 hls.js,不用 npm install,这就是 m3u8 免安装 的玩法。播放组件里读路由参数再请求视频详情:

<template> <div class="player-box"> <video ref="player" controls autoplay></video> </div> </template> <script> import axios from 'axios' export default { name: 'VideoDetail', data() { return { video: {}, hls: null } }, created() { const id = this.$route.params.id axios.get('/api/video/detail', { params: { id } }).then(res => { this.play(res.data.data.url) }) }, methods: { play(url) { const video = this.$refs.player if (url.endsWith('.m3u8')) { if (window.Hls && Hls.isSupported()) { this.hls = new Hls() this.hls.loadSource(url) this.hls.attachMedia(video) } else if (video.canPlayType('application/vnd.apple.mpegurl')) { video.src = url } } else { video.src = url } } }, beforeDestroy() { if (this.hls) this.hls.destroy() } } </script> <style scoped> .player-box { width: 100%; max-height: 480px; } </style>

created 里拿 $route.params.id,是 vue 路由参数 的标准用法;play 方法里先判断后缀,m3u8 走 hls.js,mp4 直接给 video 元素。window.Hls.isSupported() 是特性检测,而不是猜浏览器版本;iPhone 的 Safari 原生支持 m3u8,走 canPlayType 分支就行。beforeDestroy 里必须 hls.destroy(),否则切换路由后播放器还在偷偷拉分片,内存悄悄涨;组件加 scoped 样式,避免污染全局页面。hls.js 默认用 XHR 拉取 ts 分片,这些分片请求同样受跨域规则约束,第 5 章的坑就从这里来。

5. 五个高频翻车现场:SSM+Vue 点播系统排查与避坑

5.1 MySQL 8 连接报 SSL 异常或 Public Key Retrieval 错误

现象:启动 Tomcat 后控制台刷出 Communications link failure,或直接报 Public Key Retrieval is not allowed,有的还带一串 SSL 握手日志。原因:MySQL 8 默认加密插件 caching_sha2_password,SSL 默认开启,老驱动 5.1.x 不支持新协议,要么驱动版本跟不上,要么连接串没关 SSL。解决:驱动换 8.x,连接串加 useSSL=false 和 allowPublicKeyRetrieval=true,代码在第 3.3 章已经给出。这一步之后还报错,去 mysql.user 表确认 root 的 plugin 字段不是 auth_socket(Linux 上常见),是的话改成 mysql_native_password 再 FLUSH PRIVILEGES。

5.2 前端 8081 请求后端 8080:跨域 404 与 CORS 处理

现象:前端页面渲染正常,接口请求全红,浏览器控制台报 Access-Control-Allow-Origin 缺失。原因:dev 前端 8081、后端 8080 不同源,要么没配转发,要么后端没返回 CORS 头。解决:首选 vue.config.js 的 devServer 转发;如果是直接前后端分离部署,后端加一个跨域过滤器:

@WebFilter(urlPatterns = "/*", filterName = "corsFilter") public class CorsFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletResponse response = (HttpServletResponse) res; response.setHeader("Access-Control-Allow-Origin", "http://localhost:8081"); response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); response.setHeader("Access-Control-Allow-Headers", "Content-Type"); if ("OPTIONS".equals(((HttpServletRequest) req).getMethod())) return; chain.doFilter(req, res); } }

这段代码里 OPTIONS 预检请求必须直接放行,否则前端发复杂请求连业务代码都到不了。这类跨域问题很多时候像个黑匣子,排查先看 Network 面板里请求的 URL 和响应头,再决定改哪里,别一上来就改代码。

5.3 视频上传成功但页面打不开:资源映射与写死的绝对路径

现象:上传接口返回成功,数据库也插了一行,但浏览器访问视频 URL 是 404,或者本机能看、拷给别人就 404。原因:资源映射没配;数据库存了绝对路径 D:/xxx;或者文件存在项目源码目录里,重新打包被 IDE 清理。解决:统一用配置项 upload.path,资源映射 /upload/** 指向外部目录,数据库只存相对路径。mp4 拖进度条依赖 Range 请求,默认 Tomcat 静态资源没开 Range 会表现成“能播不能拖”,这算同一个坑的下半截。后悔药只有一剂:从一开始就把磁盘目录、URL 前缀、数据库字段这三处的格式分开存,别混着写。

5.4 m3u8 黑屏:CORS、Referer 和编码格式三个坑

现象:播放器黑屏转圈,Network 里 m3u8 请求返回 200,ts 分片却 403 或失败;或者浏览器直接提示不支持。原因:三个高频原因,一是 ts 分片跨域被后端 CORS 拦住,二是某些环境对 /upload 路径做了 Referer 校验,三是转码产物是 H.265/HEVC,浏览器不支持。解决:给 /upload 路径也配上 CORS 头,复用 5.2 的过滤器并把路径限定住;先单独打开一个 ts 分片的 URL,能播就排除分片本身问题;再用 ffprobe 检查编码,非 H.264 就加 -c:v libx264 重新转。

注意:排查分片问题前,先把 m3u8 和 ts 放在同一目录,路径别靠相对位置猜,直接在浏览器里打开 m3u8 看返回文本。

这个问题的排查顺序永远是“先直接打开资源,再看请求头,最后怀疑业务代码”,我因为跳步耗过一下午。

5.5 Vue 打包进后端后刷新子路由 404:history 模式与兜底

现象:dev 模式随便刷新都能重新渲染,npm run build 之后把 dist 丢进 SSM 或 Spring Boot 后端部署,点链接能进播放页,一按 F5 就 404。原因:vue-router 默认 history 模式,刷新时浏览器直接向后端请求 /video/detail/1,后端没有这个路径的路由回退规则。解决:毕设最省事的做法是改 hash 模式:

const router = new VueRouter({ mode: 'hash', routes })

URL 会变成 /#/video/detail/1,井号后面的内容不会发给服务器,刷新天然不 404。想保住 history 模式的 URL 样式,就在后端加一个兜底:所有不带 /api 和 /upload 前缀的请求,统一转发到 index.html。毕设答辩建议直接用 hash 模式,少一个配置就少一个翻车点,把精力留给功能演示。

6. 答辩前怎么验证与进阶:从跑通到能讲清楚

6.1 一张两分钟的验证清单

按正常顺序过一遍主流程:注册新用户、登录、退出、再登录;首页分类导航切换;搜索关键词模糊查视频;进播放页看播放量是否 +1;发一条评论再删除;切管理员账号,把刚上传的视频下架再上架。这七步走完,主流程没崩,答辩演示基本够用。再补三个边界:视频 url 为空时播放页要有提示,不能白屏;空评论要被拦截;连续快速点播放,播放量不能变成负数。清单里最后一项其实是考你 UPDATE 语句的写法,写成 play_count = play_count + 1 就不会负。

6.2 两个低成本进阶改造

第一个改造是给热点列表加缓存:播放量的写入已经在数据库,读热搜榜时可以引一个 ConcurrentHashMap 或 Redis,用 zset 记热度,定时回写 MySQL。答辩话术就三个词:缓存、回写、最终一致。第二个改造是上传后转码改异步:线程池处理 ffmpeg,status 字段置为转码中,前端轮询等就绪再允许播放。这两个改造正好对得上 java 开发工程师面试题里常问的并发、线程池、缓存,也是把“能跑的 demo”变成“能讲半天的项目”的最短路径。

我做这一类项目时最大的教训是:改数据库前先备份,改配置后看日志,前端报错先开 Network 再动代码。拿到任何一套源码,第一步永远是确认它的真实技术栈和数据库字符集,而不是急着点启动。很多所谓玄学问题,最后都败给一张截图和一段完整堆栈。希望帮到你。

本文还有配套的精品资源,点击获取