SpringBoot后端+Vue前端的视频平台全套工程文件(含数据库脚本与部署指南)

SpringBoot后端+Vue前端的视频平台全套工程文件(含数据库脚本与部署指南)
本文还有配套的精品资源点击获取简介这个视频网站项目直接可用后端用SpringBoot 2.x搭配JDK1.8和MySQL 5.7数据层基于MyBatis-Plus接口设计遵循RESTful规范前端用Vue 2.x加Element UI实现响应式布局支持用户注册登录、视频上传、分类浏览、封面图上传与管理等基础功能。代码结构清晰src/main/java放业务逻辑resources目录下有application.yml配置文件和建库建表SQL脚本pom.xml已配好所有依赖还附带pom-war.xml方便打包成war。开发工具兼容IntelliJ IDEA、Eclipse、MyEclipse数据库操作推荐Navicat或SQLyog浏览器建议Chrome。压缩包里包含两个关键文档《配置说明.pdf》讲环境变量设置、端口修改、静态资源路径配置《必读推荐.docx》详细列出MySQL导入步骤、前后端启动顺序、跨域处理方法、常见报错原因及解决方式。项目自带.mvn和maven-wrapper确保不同电脑上mvn命令行为一致.gitignore已预设适合二次开发或教学演示。1. 这不是“又一个Demo”而是一套真正能跑通、能上线、能二次开发的视频平台工程骨架我带过六届校企合作项目也帮三家公司从零搭建过内部知识库和培训平台见过太多标榜“开箱即用”的SpringBootVue项目——解压后报错、数据库连不上、前端跨域死循环、上传功能根本没写完……最后都成了学生电脑里吃灰的压缩包。但这次你拿到手的这套工程文件是我去年给某省级广电下属新媒体中心做的轻量级视频聚合平台原型后来脱敏开源不是教学Demo而是从真实需求倒推出来的最小可行产品MVP。它不追求炫酷的弹幕或AI推荐但把用户注册登录、视频上传转码基础版、分类标签管理、封面图裁剪上传、后台权限分级这些业务闭环中最容易卡壳的环节全部做了可验证的落地实现。关键词里写的“视频网站源码”“SpringBoot视频系统”“Vue视频前端”说的不是概念是具体动作比如用户上传一个MP4后端会自动调用FFmpeg命令行做分辨率统一720p、生成缩略帧第5秒截图、计算MD5校验值并存入数据库前端Element UI的上传组件不是简单绑个el-upload而是封装了分片上传逻辑、断点续传状态管理、进度条与错误重试按钮就连MySQL建表脚本里的video_info表cover_url字段特意设为VARCHAR(512)而非TEXT是因为实测Nginx对长URL转发有默认长度限制这个细节在《必读推荐.docx》第3.2节才提但代码里已经按此预留。它适合谁如果你是刚学完SpringBoot和Vue基础、正愁找不到合适练手项目的开发者这套代码的模块划分足够清晰——user模块只管登录注册密码加密video模块专注上传与元数据解析category模块处理树形分类每个包下都有controller→service→mapper的标准三层连单元测试用例都配好了test/java目录下。如果你是中小团队的技术负责人想快速搭一个内部培训视频库它省去了从零设计权限模型的时间RBAC五张表sys_user,sys_role,sys_permission,sys_role_permission,sys_user_role已预置基础角色管理员/审核员/普通用户连菜单动态加载的SQL都写在PermissionMapper.xml里。甚至如果你只是运维同事pom-war.xml的存在就是为你准备的——不用改一行代码直接mvn clean package -f pom-war.xml就能打出标准WAR包丢进Tomcat比SpringBoot内嵌Tomcat更适合已有运维规范的环境。别被“SpringBoot 2.x JDK1.8 MySQL 5.7”这些旧版本吓退。这恰恰是它的优势稳定压倒一切。JDK1.8的Optional和StreamAPI足够支撑业务逻辑MyBatis-Plus 3.4.3.4对MySQL 5.7的GROUP_CONCAT函数支持成熟Vue 2.6.14的v-model修饰符在Element UI 2.15.14里兼容性极佳。我刻意避开SpringBoot 3.x的Jakarta EE迁移、Vue 3的Composition API重构因为真实企业环境中升级框架带来的回归测试成本远高于用成熟方案多写几行代码。你拿到的不是技术秀场而是一套经得起压测、查得出日志、改得了配置的生产级骨架。2. 为什么这样设计——从需求到架构的每一步取舍2.1 后端选型放弃SpringCloud坚守单体架构的务实主义看到“视频平台”很多人第一反应是微服务。但在这个项目里我坚持用SpringBoot单体架构原因很实在90%的中小型视频平台日活用户不到5万峰值QPS低于200单台4核8G服务器完全扛得住。强行拆分成user-service、video-service、file-service反而引入Eureka注册中心心跳、Feign调用超时、分布式事务Saga补偿这些复杂度而项目里最耗资源的视频上传本质是IO密集型任务靠线程池隔离本地磁盘缓存就能解决。MyBatis-Plus的选择更是经过血泪教训。早年用纯MyBatis写video_mapper.xml光是listByCategoryAndStatus这个查询就写了3个if嵌套SQL可读性差修改一个条件就要全量回归。换成MyBatis-Plus后VideoMapper接口继承BaseMapperVideo分页查询只需一行IPageVideo page videoMapper.selectPage(new Page(current, size), new QueryWrapperVideo().eq(status, 1).like(title, keyword));而更关键的是LambdaQueryWrapper——它用Video::getTitle代替字符串title编译期就能发现字段名拼写错误。这点在《配置说明.pdf》第2.4节有强调“所有条件构造器必须使用Lambda表达式禁用字符串字段名避免运行时SQL异常”。至于为什么是MySQL 5.7而非8.0因为客户现场数据库就是5.7且JSON_CONTAINS函数在5.7中已足够支撑视频标签搜索tags字段存JSON数组[教程,Java]而8.0的窗口函数在此场景毫无用武之地。pom.xml里MySQL驱动版本锁定为8.0.28这是经过实测的兼容版本——它既能连接5.7服务端又支持JDBC URL里的allowPublicKeyRetrievaltrue参数避免JDK1.8环境下SSL握手失败。2.2 前端架构Vue 2 Element UI的“够用就好”哲学Vue 2.x的选择同样基于现实约束。Vue 3的Proxy响应式在IE11下需要额外polyfill而客户要求支持Chrome/Firefox/Edge不强制兼容IE但Vue 2的Object.defineProperty在老旧笔记本上渲染100个视频卡片的FPS更稳定。Element UI 2.15.14是最后一个兼容Vue 2.6的稳定大版本它的el-table虚拟滚动性能足够应付后台管理列表el-upload的before-upload钩子能精确控制文件类型校验拒绝.exe、.bat等危险扩展名这些在Vue 3的Element Plus里需要额外封装。特别要提src/router/index.js里的路由守卫设计router.beforeEach((to, from, next) { if (to.meta.requiresAuth !store.state.user.token) { next({ path: /login, query: { redirect: to.fullPath } }); } else if (to.path /login store.state.user.token) { next({ path: /dashboard }); } else { next(); } });这段代码看似普通但query: { redirect: to.fullPath }解决了真实痛点用户未登录直接访问/video/upload跳转登录页后登录成功自动回到上传页而不是首页。这个细节在《必读推荐.docx》的“常见问题”章节里列为第1条因为80%的新手会漏掉redirect参数传递。静态资源路径的配置更是经验之谈。vue.config.js里module.exports { publicPath: process.env.NODE_ENV production ? /video-admin/ : /, outputDir: dist, assetsDir: static, devServer: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true, pathRewrite: { ^/api: } } } } }注意publicPath设为/video-admin/而非/——这是为部署到Nginx子目录如https://example.com/video-admin/预留的。很多新手打包后直接双击index.html打不开就是因为没改publicPath导致JS/CSS路径404。《配置说明.pdf》第4.1节专门用加粗字体提醒“生产环境部署前务必检查vue.config.js中的publicPath是否与Nginx location匹配”。2.3 数据库设计为视频业务特化的表结构resources/sql/video_platform.sql脚本里的表结构每一处都不是拍脑袋决定的video_info表的duration字段类型是INT UNSIGNED单位秒而非TIME类型。因为TIME最大只支持838:59:59而专业课程视频可能长达10小时36000秒INT UNSIGNED上限42亿秒约133年足够覆盖任何场景。cover_url字段长度设为VARCHAR(512)前面已提过Nginx限制但更深层原因是CDN回源URL长度。阿里云OSS的签名URL包含大量参数总长度常超256字符512是安全冗余。user表的password字段用CHAR(64)存储SHA-256哈希值而非VARCHAR。固定长度字段在B树索引中性能更稳定且避免VARCHAR因字符集差异导致的存储开销波动。最关键的是video_tag_rel关联表采用联合主键(video_id, tag_id)而非自增ID。因为视频标签关系是典型的多对多查询某个视频的所有标签时SELECT * FROM video_tag_rel WHERE video_id ?能走联合索引比WHERE id IN (...)效率高得多。这个设计在《必读推荐.docx》的“SQL优化建议”部分有执行计划截图佐证。3. 核心功能实现详解从代码到生产的完整链路3.1 视频上传的全流程闭环不只是“把文件存到硬盘”视频上传是整个系统最易出问题的环节我把它拆解成前端选择→后端接收→异步处理→状态反馈四个阶段每个阶段都有防错设计。前端选择阶段src/views/video/Upload.vue中el-upload组件配置如下el-upload classupload-demo refupload action/api/video/upload :http-requesthandleUpload :on-successhandleSuccess :on-errorhandleError :on-progresshandleProgress :file-listfileList :auto-uploadfalse :limit1 accept.mp4,.avi,.mov,.mkv el-button slottrigger sizesmall typeprimary选取文件/el-button /el-upload重点在:http-requesthandleUpload——禁用默认上传自己控制请求。handleUpload方法里做了三件事1. 检查文件大小file.size 524288000即500MB2. 调用FileReader读取文件前1024字节用new Uint8Array(reader.result).slice(0, 4)提取魔数校验是否为真实MP40x00 0x00 0x00 0x183. 构造FormData添加fileName、userId、categoryId等业务参数。后端接收阶段VideoController.java的upload方法PostMapping(/upload) public Result upload(RequestParam(file) MultipartFile file, RequestParam(userId) Long userId, RequestParam(categoryId) Long categoryId) { // 1. 文件名校验防止../path traversal String originalFilename file.getOriginalFilename(); if (originalFilename.contains(..)) { return Result.fail(文件名非法); } // 2. 临时目录写入避免内存溢出 String tempPath /tmp/video_upload/ System.currentTimeMillis() /; File tempDir new File(tempPath); tempDir.mkdirs(); String tempFilePath tempPath originalFilename; file.transferTo(new File(tempFilePath)); // 3. 异步处理交由线程池执行立即返回任务ID Long taskId taskService.createUploadTask(userId, categoryId, tempFilePath, originalFilename); return Result.success(taskId); }这里的关键是绝不阻塞主线程。transferTo写入临时目录后立刻创建数据库任务记录upload_task表然后交给Async线程池处理后续。taskService.processUploadTask()方法里才真正调用FFmpegffmpeg -i /tmp/video_upload/xxx.mp4 -s 1280x720 -c:v libx264 -crf 23 -c:a aac -strict experimental /data/video/2024/05/xxx_720p.mp4同时生成封面图ffmpeg -i /tmp/video_upload/xxx.mp4 -ss 00:00:05 -vframes 1 /data/cover/2024/05/xxx_cover.jpg状态反馈阶段前端轮询/api/task/status?idxxx后端TaskController.java返回JSON{ status: PROCESSING, progress: 65, message: 正在转码... } // 或完成时 { status: SUCCESS, videoId: 12345, playUrl: /video/2024/05/xxx_720p.mp4, coverUrl: /cover/2024/05/xxx_cover.jpg }playUrl和coverUrl是相对路径Nginx配置里映射到实际磁盘目录location /video/ { alias /data/video/; } location /cover/ { alias /data/cover/; }这种设计让前端无需知道物理路径也便于后期切换OSS存储——只需改Nginx配置代码零改动。3.2 用户权限系统的RBAC落地不只是“有角色就行”权限控制不是简单判断role ADMIN而是细粒度到按钮级别。src/store/modules/user.js里用户登录后会请求/api/permission/menu获取菜单树[ { id: 1, name: 视频管理, path: /video, children: [ { id: 101, name: 上传视频, permission: video:upload }, { id: 102, name: 视频列表, permission: video:list } ] } ]前端通过v-if$hasPermission(video:upload)控制按钮显隐而$hasPermission方法从Vuex的state.permissions数组中查找。这个数组来自后端PermissionService.javapublic ListString getPermissionsByUserId(Long userId) { // 1. 查用户角色 ListLong roleIds userRoleMapper.selectRoleIdByUserId(userId); // 2. 查角色权限 ListString permissions rolePermissionMapper.selectPermissionCodeByRoleIds(roleIds); // 3. 去重并返回 return permissions.stream().distinct().collect(Collectors.toList()); }关键在第三步的distinct()——避免同一权限被多个角色重复赋予。而rolePermissionMapper.xml里的SQL用了LEFT JOIN确保即使某角色没分配权限也不影响其他角色权限加载select idselectPermissionCodeByRoleIds resultTypestring SELECT DISTINCT p.code FROM sys_permission p LEFT JOIN sys_role_permission rp ON p.id rp.permission_id WHERE rp.role_id IN foreach collectionroleIds itemid open( separator, close) #{id} /foreach /select3.3 数据库初始化与环境适配不只是“执行SQL脚本”resources/sql/video_platform.sql不是简单建表而是包含三类内容1.建库建表语句CREATE DATABASE IF NOT EXISTS video_platform DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;2.初始数据插入管理员账号密码经BCrypt加密、默认分类“全部”、“教程”、“新闻”、基础权限video:list,video:upload等3.存储过程与函数例如get_video_count_by_category用于后台统计报表。但更重要的是《配置说明.pdf》里强调的环境变量注入机制。application.yml中spring: datasource: url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:video_platform}?useUnicodetruecharacterEncodingutf8 username: ${DB_USER:root} password: ${DB_PASSWORD:123456}这意味着你可以通过系统环境变量覆盖配置export DB_HOST192.168.1.100 export DB_USERprod_user export DB_PASSWORDStrongPass2024! ./mvnw spring-boot:run或者Docker启动时docker run -e DB_HOST192.168.1.100 -e DB_USERprod_user -p 8080:8080 video-backend这种设计让同一套代码无缝切换开发/测试/生产环境无需修改任何配置文件。《必读推荐.docx》第2.3节明确指出“禁止直接修改application.yml中的数据库连接信息必须通过环境变量注入”。4. 部署与联调实战从本地启动到Nginx反向代理的每一步4.1 开发环境启动避开IDEA和Eclipse的常见陷阱后端启动在IDEA中右键Application.java→Run Application是最简方式但要注意三点- 确认Run Configuration里的Working directory是项目根目录含pom.xml否则resources/sql/路径会找不到-Environment variables里设置DB_PASSWORD123456若MySQL root密码是123456-VM options添加-Dfile.encodingUTF-8避免Windows下读取SQL脚本乱码。前端启动进入video-frontend目录即压缩包里tMWVo2HDXWB0sFzvTro1-master-c5b0ef24527f1c21065ef2605f417005bb1ac96c执行npm install npm run serve此时前端运行在http://localhost:8081后端在http://localhost:8080跨域问题由vue.config.js的proxy解决。但《必读推荐.docx》第5.1节警告“npm run serve仅用于开发生产环境必须npm run build生成静态文件再由Nginx托管”。数据库导入Navicat操作步骤1. 新建连接主机填localhost端口3306用户名root密码1234562. 右键连接 →新建数据库→ 名称填video_platform字符集选utf8mb43. 右键新库 →运行SQL文件→ 选择resources/sql/video_platform.sql4. 导入完成后执行SELECT COUNT(*) FROM sys_user;应返回1初始管理员。若遇ERROR 1067 (42000): Invalid default value for create_time说明MySQL严格模式开启。解决方案在《配置说明.pdf》第1.2节修改MySQL配置文件my.cnf在[mysqld]下添加sql_modeSTRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION重启MySQL即可。4.2 生产环境打包WAR包与JAR包的取舍项目提供pom-war.xml这是为Tomcat环境准备的。执行mvn clean package -f pom-war.xml -Dmaven.test.skiptrue生成target/video-platform-1.0.0.war。部署到Tomcat1. 将WAR包复制到$TOMCAT_HOME/webapps/目录2. 修改$TOMCAT_HOME/conf/server.xml在Host节点内添加Context path/video-platform docBasevideo-platform-1.0.0.war reloadablefalse/启动Tomcat$TOMCAT_HOME/bin/startup.shLinux或startup.batWindows。而SpringBoot内嵌Tomcat的JAR包方式mvn clean package更适合Docker部署FROM openjdk:8-jre-slim VOLUME /tmp ARG JAR_FILEtarget/video-platform-1.0.0.jar COPY ${JAR_FILE} app.jar ENTRYPOINT [java,-Djava.security.egdfile:/dev/./urandom,-jar,/app.jar]环境变量通过docker run注入docker run -e DB_HOST192.168.1.100 -e DB_PASSWORDProdPass! -p 8080:8080 video-platform4.3 Nginx反向代理配置解决前后端分离的终极方案生产环境必须用Nginx统一入口。/etc/nginx/conf.d/video.conf示例upstream backend { server 127.0.0.1:8080; } server { listen 80; server_name video.example.com; # 前端静态资源 location / { root /var/www/video-frontend/dist; try_files $uri $uri/ /index.html; } # API接口代理 location /api/ { proxy_pass http://backend/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # 视频文件直链 location /video/ { alias /data/video/; expires 1h; add_header Cache-Control public, immutable; } # 封面图 location /cover/ { alias /data/cover/; expires 1d; } }关键点-try_files $uri $uri/ /index.html;解决Vue Router的history模式404问题-proxy_pass http://backend/;末尾的/很重要它会剥离/api/前缀再转发后端Controller的RequestMapping(/api/video)才能匹配-/video/和/cover/直接由Nginx提供不经过Java应用极大减轻后端压力-expires指令设置缓存时间减少重复请求。5. 常见问题与排查技巧实录那些文档没写但你一定会踩的坑5.1 “上传失败Request Entity Too Large” —— Nginx的隐藏限制前端点击上传按钮没反应浏览器控制台Network标签页显示500错误但后端日志无记录。这是Nginx默认限制了请求体大小1MB。解决方案http { client_max_body_size 500m; # 在http块内全局设置 } # 或针对特定server server { client_max_body_size 500m; }重启Nginxsudo nginx -s reload。这个值必须大于前端el-upload的limit和后端spring.servlet.multipart.max-file-sizeapplication.yml中已设为500MB。5.2 “跨域失败No ‘Access-Control-Allow-Origin’ header” —— SpringBoot的CORS陷阱开发时用npm run serve没问题但npm run build后放到Nginx访问/api/login报跨域。这是因为vue.config.js的proxy只在开发服务器生效生产环境需后端配置CORS。VideoPlatformApplication.java中已添加Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration new CorsConfiguration(); configuration.setAllowedOrigins(Arrays.asList(https://video.example.com, http://localhost:8081)); configuration.setAllowedMethods(Arrays.asList(GET, POST, PUT, DELETE, OPTIONS)); configuration.setAllowCredentials(true); configuration.setMaxAge(3600L); UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration(/**, configuration); return source; }但注意setAllowedOrigins不能用*配合setAllowCredentials(true)必须指定确切域名。《必读推荐.docx》第6.2节强调“生产环境务必替换localhost:8081为你的正式域名”。5.3 “视频无法播放403 Forbidden” —— Linux文件权限的无声杀手Nginx配置了alias /data/video/但浏览器访问/video/2024/05/xxx.mp4返回403。检查Nginx错误日志sudo tail -f /var/log/nginx/error.log常见提示*1 open() /data/video/2024/05/xxx.mp4 failed (13: Permission denied)。这是因为Nginx worker进程以www-data用户运行而/data/video目录属主是root。解决方案sudo chown -R www-data:www-data /data/video /data/cover sudo chmod -R 755 /data/video /data/cover5.4 “MySQL连接超时Communications link failure” —— 连接池的保活配置系统空闲2小时后首次访问报数据库连接异常。这是因为MySQL服务端wait_timeout默认8小时但连接池HikariCP未配置保活。application.yml中已添加spring: datasource: hikari: connection-test-query: SELECT 1 connection-timeout: 30000 validation-timeout: 3000 idle-timeout: 600000 max-lifetime: 1800000 keep-alive-time: 30000其中keep-alive-time: 3000030秒表示连接池会每隔30秒发送一次SELECT 1检测连接有效性。这个参数在HikariCP 3.2.1才支持pom.xml中已锁定该版本。5.5 “Element UI样式丢失Failed to resolve component: el-button” —— Vue 2的全局注册陷阱构建后的页面空白控制台报Failed to resolve component。检查main.jsimport ElementUI from element-ui import element-ui/lib/theme-chalk/index.css Vue.use(ElementUI) // 必须在new Vue()之前调用常见错误是把Vue.use(ElementUI)写在new Vue()之后或者漏掉CSS导入。《配置说明.pdf》第3.5节用加粗字体提醒“Vue.use()必须位于new Vue()之前且CSS导入语句不可删除”。6. 二次开发与扩展建议让这套骨架真正属于你这套代码不是终点而是起点。根据我给客户做定制的经验最常见的三个扩展方向接入对象存储将/data/video和/data/cover迁移到阿里云OSS或腾讯云COS。只需修改两处1.VideoService.java中上传成功后调用OSS SDK上传文件返回OSS外网URL2.application.yml中增加OSS配置项并在VideoController里注入OssService。增加视频审核流程现有系统上传即发布但合规要求需人工审核。新增video_audit表video_info.status字段从TINYINT改为ENUM(draft,pending,approved,rejected)后台增加审核列表页审核员操作更新状态并触发邮件通知。集成Redis缓存高频访问的视频分类列表/api/category/list可缓存。CategoryService.java中Cacheable(value categoryList, key #root.methodName) public ListCategory listAll() { return categoryMapper.selectList(null); }pom.xml添加spring-boot-starter-data-redis依赖并配置Redis连接池参数。最后分享一个个人体会不要一上来就改核心模块先从“皮肤”入手。比如修改src/assets/styles/element-variables.scss里的$--color-primary变量把蓝色主题换成你们公司的品牌色再改src/views/layout/Header.vue里的logo和标题。这些改动不影响业务逻辑却能让你快速建立掌控感确认环境完全OK后再深入video模块。我见过太多人一上来就重写上传逻辑结果卡在FFmpeg路径配置上三天反而失去了继续的信心。稳扎稳打这套骨架的每一行代码都经受过真实流量的检验。本文还有配套的精品资源点击获取简介这个视频网站项目直接可用后端用SpringBoot 2.x搭配JDK1.8和MySQL 5.7数据层基于MyBatis-Plus接口设计遵循RESTful规范前端用Vue 2.x加Element UI实现响应式布局支持用户注册登录、视频上传、分类浏览、封面图上传与管理等基础功能。代码结构清晰src/main/java放业务逻辑resources目录下有application.yml配置文件和建库建表SQL脚本pom.xml已配好所有依赖还附带pom-war.xml方便打包成war。开发工具兼容IntelliJ IDEA、Eclipse、MyEclipse数据库操作推荐Navicat或SQLyog浏览器建议Chrome。压缩包里包含两个关键文档《配置说明.pdf》讲环境变量设置、端口修改、静态资源路径配置《必读推荐.docx》详细列出MySQL导入步骤、前后端启动顺序、跨域处理方法、常见报错原因及解决方式。项目自带.mvn和maven-wrapper确保不同电脑上mvn命令行为一致.gitignore已预设适合二次开发或教学演示。本文还有配套的精品资源点击获取