SpringBoot+Vue校园资料分享平台:权限控制与分片上传实践
2026/9/9 21:08:04 网站建设 项目流程

校园里找资料有多痛苦,估计每个经历过期末的人都懂:课程PPT散落在十几个群聊里,学长学姐的笔记存在个人网盘链接且经常失效,想找一份往年试卷得靠人品。做这个前后端分离的校园资料分享平台,初衷就是把这堆烂摊子收拢成一个可检索、有权限、能追溯的系统,技术栈选了SpringBoot+Vue+MyBatis+MySQL这套经典组合。

这篇文章把这套系统的完整设计和实现思路写透了,从数据库怎么设计、权限怎么控制,到文件上传怎么处理大文件、Nginx怎么配置,全部基于可运行的源码展开。适合正在做课程设计、毕业设计的在校生,也适合想拿一个完整前后端分离项目练手的初中级开发者。你跟着文章的思路走一遍,不仅能把这个平台跑起来,还能搞清楚每个模块为什么这么设计。

1. 需求梳理:这个平台要解决的四个核心问题

很多人一上来就建表写接口,做到一半发现权限乱了、文件传不上去、搜索查不到东西,本质上是动手前没把需求想清楚。这个校园资料分享平台的目标用户是学生、教师和管理员三类角色,对应的核心诉求各不相同。学生要能快速找到资料、下载资料;教师需要能上传教学资料并管理自己发布的内容;管理员则要负责用户审核、内容合规检查、分类维护这些事情。

围绕这三类角色,平台需要解决的四个核心问题是。

问题一:资料的组织方式。海量课件、试卷、笔记必须要有清晰的多级分类体系。单一层级不够用,比如“计算机学院”下面还有“数据结构”“操作系统”这样的课程维度。设计上采用父分类+子分类两级结构,必要时可以扩展到三级。

问题二:访问权限的控制。校园平台不同于公开网盘,有些资料只对校内用户开放,有些资料是教师指定班级可见。权限控制的粒度至少要达到“未登录用户不可下载、普通用户受每日下载次数限制、管理员可管理所有内容”这个级别。这里涉及典型的RBAC(基于角色的访问控制)模型。

问题三:大文件传输的可靠性。教学视频动辄几百MB甚至上GB,常规的HTTP上传会面临超时、中断后重传成本高等问题。前端直传+后端接收这种简单方案在校园网环境下根本扛不住,必须设计分片上传和断点续传机制。

问题四:内容的可检索性。资料存进去之后要能被找到,标题搜索是底线,如果能按课程名、教师名、资料类型组合筛选会更实用。搜索功能看似简单,实际做起来有不少细节,后面会专门展开。

这四个问题对应到系统模块上,就是用户认证、分类管理、资料管理、文件上传下载、搜索、数据统计等几大板块。下面这张功能清单表格可以作为设计阶段的参考。

模块功能点服务对象
用户认证注册、登录、JWT鉴权学生、教师
用户管理用户列表、状态禁启用、角色分配管理员
分类管理多级分类增删改查管理员
资料管理资料上传、编辑、审核上下架教师、管理员
文件传输分片上传、断点续传、进度显示学生、教师
搜索筛选关键字标题搜索、多条件组合筛选所有用户
下载管理权限校验、每日次数限制、记录留痕学生
数据看板上传量、下载量、活跃用户统计管理员

回到"前后端分离"这个架构选择上。校园项目的场景是学生用户数据量不大但访问时间段集中,比如期末周突然有很多人同时上线下资料。前后端分离的好处在于前端静态资源可以走CDN或Nginx缓存,后端只提供API,在高并发下载场景下能有效分担服务器压力。同时Vue前端可以单独部署调试,不需要每次改样式都重启Java进程,开发体验好很多。

2. 技术选型的底层逻辑:为什么是这套组合而非其他

这套技术组合在社区里讨论度一直很高,总有人问为什么不选更"新潮"的框架。这里把我实际对比后的思考过程完整梳理一遍,免得你在同样的问题上反复纠结。

2.1 SpringBoot:项目落地的效率关键

SpringBoot的核心价值是"约定大于配置"。做这个校园平台时,我只需要引入spring-boot-starter-web就能得到内嵌Tomcat的Web环境,引入spring-boot-starter-jdbc配合MyBatis就能完成数据访问层的搭建。没有繁琐的XML配置文件需要维护,这对小团队、课程设计、毕业设计这类人力有限的场景特别友好。

它内置的自动配置机制在处理常规需求时优势明显。比如数据库连接池,引入spring-boot-starter-jdbc后默认的HikariCP就是性能很好的连接池选型,不需要额外调优就能扛住校园场景的并发量。再比如统一异常处理,配合@RestControllerAdvice注解可以非常优雅地封装返回结构,避免每个Controller里都写try-catch。

值得注意的是版本选择。SpringBoot 2.x和3.x在底层上有较大差异,3.x基于Jakarta EE规范,包名从javax迁移到jakarta。对于这个校园项目,我选择了2.7.x这个相对成熟的版本,生态兼容性好,网上资料也多,遇到问题好排查。如果你是非科班或者刚接触SpringBoot,建议也先用2.7.x跑通再考虑升级。

2.2 MyBatis:手动控制SQL是有意为之

选MyBatis而不选MyBatis-Plus,是在这个项目里反复权衡的结果。很多快速开发框架会推荐直接用MyBatis-Plus,因为它提供了BaseMapper的CRUD方法,写代码确实快。但校园资料分享平台涉及大量自定义的查询逻辑——多表关联查资料与分类、统计下载次数排行、按时间区间聚合数据——这些场景用MyBatis-Plus的LambdaQueryWrapper反而绕,不如直接写SQL直观。

MyBatis的另一个不可替代的价值在于SQL的可控性。当你需要优化一条大表查询时,直接在XML里改SQL即可,不用去理解框架底层如何拼接条件。比如搜索功能里的动态SQL,使用 标签根据前端传参拼装查询条件,语义非常清晰。

这里要提一个容易踩的性能坑:MyBatis的一级缓存默认开启且作用域是SqlSession,这在Spring结合MyBatis使用时通常意味着一次请求一个SqlSession,问题不大。二级缓存则需要谨慎,如果开启了二级缓存并且缓存了查询结果,数据更新后如果清理时机不对,就会出现脏读。通常在校园项目里数据量不大,没必要开二级缓存,减少一个隐患。

2.3 Vue + Element-UI:快速搭建后台管理系统

前端选型其实在Vue2+Element-UI和Vue3+Element-Plus之间纠结过一阵。考虑到这套系统面向教务资料管理场景,核心页面是表格、表单、上传组件这类中后台界面,用Element系列组件库效率最高。抱着稳妥的态度,我用了Vue2 + Element-UI组合。

Vue的核心优势是数据驱动视图,配合Vuex做全局状态管理、Vue Router做页面路由,前端工程结构可以组织得相当清晰。比如用户登录后的token存储、路由守卫里的权限验证、axios实例的统一封装,这些跨页面的逻辑在Vue的生态里都有成熟的解决方案。

Vue里有一个常用但容易出问题的点: 组件缓存。在这个项目里,资料列表组件和数据看板组件需要缓存,否则切tab再切回来要重新加载数据;但分类管理组件不能缓存,因为每次进入都需要拉取最新分类。这需要在路由配置的meta字段里控制是否需要缓存,实际开发中很多人忽略这个细节导致列表状态错乱。

2.4 MySQL:版本选择与字符集配置

MySQL选用8.0版本。8.0在窗口函数、CTE、默认字符集utf8mb4等特性上比5.7好不少,而且8.0社区版对开发者免费,学校服务器部署也顺利。如果非要用5.7,注意默认字符集是latin1,建库时要手动指定utf8mb4,否则中文存储会出问题。

MySQL 8.0里一个需要注意的配置是数据库连接串的时区参数。如果连接串里不加serverTimezone=Asia/Shanghai,会因为时区差异报错。更隐蔽的一个坑是MySQL 8.0的caching_sha2_password认证插件,某些版本的客户端驱动不兼容,需要在创建用户时指定mysql_native_password。我在项目中用到的连接串配置如下。

spring.datasource.url=jdbc:mysql://localhost:3306/campus_share?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai&useSSL=false spring.datasource.username=root spring.datasource.password=yourpassword spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

3. 数据库设计:表结构如何支撑权限、分类与文件元数据

数据库是这套系统的地基。表结构设计得合理,后面写业务代码会非常顺手;设计不合理,后面每个模块的查询都会变得别扭。这里把核心表结构的设计思路拆开说清楚。

3.1 五张核心表的关系梳理

这套系统的核心表可以概括为:用户表、分类表、资料表、文件表、下载记录表。这五张表之间的关系是整个业务逻辑的骨架。

用户表存储账号信息,包括用户名、密码(BCrypt加密存储)、真实姓名、角色字段、状态字段。角色用简单的字符串区分,比如ROLE_STUDENT、ROLE_TEACHER、ROLE_ADMIN,没有引入复杂的权限表——因为这个平台的角色数量少且权限固定,用admin/user两个核心值加状态控制就够了。认证采用JWT方案,登录成功后服务端签发token,后续请求在拦截器中校验。

分类表设计成自关联结构,parent_id字段指向自身主键,实现无限级分类。例如"计算机学院"下面挂"数据结构""操作系统"两个子节点。前端渲染多级分类菜单时通过递归组件处理。

资料表是整个平台的核心表,字段包括标题、描述、分类、上传者、文件地址、文件大小、下载次数、审核状态等。这里要特别处理的是文件地址:不直接存物理路径,而是存业务路径——即文件的相对URL,例如/uploads/2024/05/12/uuid_数据结构.pdf。这样便于后续搬迁存储位置,只要保持相对路径不变,数据库不需要改动。

文件表则细化到文件层,记录文件名、存储名、MD5值、大小、上传者等。这里为什么要单独拆一张文件表而不是和资料表合并?因为同一个MD5的文件如果被操作人员重复上传,可以通过MD5值去重,避免存储浪费。文件表的MD5字段加上唯一索引,上传时先查MD5,如果已存在直接复用,节省服务器磁盘空间。

下载记录表负责记录每次下载行为,包括下载者ID、资料ID、下载时间、IP地址等。这个表有两个作用:限量控制的基础数据来源,以及平台运营的审计日志。关联查询时,用资料ID和时间范围就能算出某份资料的热度趋势。

3.2 索引设计:高频查询路径的优化选择

资料列表页最常见的查询是"按分类查最新资料"和"按关键字查标题",类目中心是资料表。这两类查询的索引设计非常直接:category_id字段加普通索引,因为分类筛选是高频且选择性较好的查询条件;download_count字段也值得加索引,用于排序下载排行榜。

下载记录表是一个增长很快的表,索引设计更要谨慎。resource_iduser_id各加普通索引,用于快速验证某人是否下载过某资料;download_time加索引用于时间范围统计。

有必要提醒一个容易忽视的点:不要在一张表上加超过5-6个索引。索引不是越多越好,因为每次INSERT、UPDATE都要维护索引树,写入会变慢。校园项目数据量撑死几十万条,保证高频查询路径有覆盖就够了,不需要为了理论上的完美把所有字段都塞进索引。

3.3 文件存储与数据库的配合

文件本身不存数据库,存服务器磁盘或对象存储,数据库只记录元数据。这套设计的核心思想是"数据与文件分离"。文件上传接口接收前端分片后,先按日期分目录存储,文件名重命名为UUID,保证不重名,然后记录到文件表。资料表里的file_url字段则指向这个存储的相对路径。

文件上传成功后,响应体返回文件ID和URL,前端把这两个值随资料表单一起提交。后端创建资料记录时,校验文件存在性与归属,完成业务闭环。这个流程看似简单,但顺序很重要:先传文件后创建资料,避免资料记录指向一个不存在的文件。中途如果资料创建失败,文件会变成孤儿文件,需要定时任务清理上传目录中超时未关联的记录,我在项目中加了一个每天凌晨执行的清理任务,删除队龄超过24小时且未关联资料的文件记录。

4. 后端核心实现:权限、分片上传与检索排序

后端代码是整套系统的工作核心。这一节把三个最核心的实现详细展开,分别是JWT权限认证流程、多线程分片上传、以及搜索排序的优化方案。

4.1 JWT认证与权限拦截器链

JWT方案的选择很明确:无状态认证适合前后端分离,服务端不存储会话信息,扩展性好。依赖引入jjwt库,工具类封装生成与解析方法。生成token时把用户ID和角色放到claims里,然后设置过期时间,常规设为7天,校园场景比较适合一周免登录,过期后需要重新登录。

拦截器的实现共分三步。第一步,自定义一个HandlerInterceptor实现类,重写preHandle方法;第二步,从请求头Authorization中获取token并进行解析;第三步,将用户信息放入ThreadLocal供后续业务使用。这里有一个细节:白名单路径要放行——登录、注册、验证码、公开资源的路径不应该进入拦截器,否则用户没登录连登录页面都调不了接口。

权限校验方面,业务接口上加自定义注解@RequireRole("ROLE_ADMIN"),配合拦截器中的逻辑判断。实现方式是在拦截器中读出用户角色,再检查当前请求路径对应的注解要求。这样做比在每个Controller方法里写if判断要简洁得多。上传资料和删除资料都是需要校验身份的操作,前者要求登录用户,后者仅限管理员。

这里有一个非常值得注意的Java层问题:ThreadLocal保存用户信息,一次请求一个线程,接口结束后必须remove掉,否则在Tomcat线程池复用的场景下,线程复用时ThreadLocal里还残留上一个用户的信息,可能造成用户A的操作被记录到用户B头上。我实际踩过这个坑,排查了半个晚上才发现是ThreadLocal没清理导致的。

4.2 大文件分片上传:前后端联动的完整链路

教学视频通常有几百MB,这是Web上传最难受的场景。大部分同学做项目时只处理几MB的PDF和PPT,不会意识到大文件上传的复杂度。这里把完整实现梳理一遍。

前端使用Vue结合vue-simple-uploader组件,它封装了分片上传、秒传、断点续传等能力。组件将文件切片后,依次请求后端接口上传每个分片,所有分片传完后通知后端合并成完整文件。核心参数是chunkSize,我设置为5MB——太大会导致单个分片传输仍容易超时,太小时分片数量过多,请求频繁效率低下,5MB是实践中比较均衡的值。

后端提供三个核心接口:

  1. 初始化上传:接收文件名、文件MD5,判断文件是否已存在。已存在直接返回秒传标识,前端不再传任何分片。
  2. 上传分片:接收当前分片索引和分片数据,写入临时临时目录。
  3. 合并分片:校验分片完整性,将分片按顺序合并成新文件,计算完整文件MD5并记录到文件表。

分片上传接口有一个高频难题:分片顺序。由于前端是并发上传分片的,接口收到的分片顺序不一定是按索引递增的,因此合并接口必须按分片索引排序后再拼接字节流。同时每个分片在临时目录中的命名要包含分片索引,比如{uploadId}_{chunkIndex}.part。合并完成后清理临时目录。

上传过程中的并发安全问题也不能忽视。同一文件同时发起分片上传请求,后端需要保证创建的上传上下文是唯一的。解决方式是用ConcurrentHashMap维护一个uploadId到上传上下文的映射,下次请求直接复用上下文。

分片上传期间的跨域问题也值得提。前后端分离项目,前端在8080端口、后端在8081端口就必然有跨域请求。生产环境用Nginx反向代理能解决跨域,但开发阶段最简单的办法是在后端增加CORS配置,允许指定来源访问。开发环境可以宽松一点,允许http://localhost:8080即可。

4.3 全文检索优化:从LIKE到中文分词的渐进方案

搜索模块一开始用的MySQL LIKE模糊匹配,资料量几百条时体验还能接受,但当数据量上万条,LIKE '%关键字%'的查询性能会急剧下降,因为这种写法无法利用索引。校园项目虽然数据量通常不大,但搜索体验是用户感知最强的模块,值得做得更好。

简单的优化是先加全文索引:MySQL 8.0的全文索引支持中文,但默认分词对中文支持并不理想。更实用的方案是引入ElasticSearch做独立的搜索服务,但这对校园级项目又显得过重了——要额外维护一套ElasticSearch服务,部署复杂度会上升不少。

折中的方案是构建一个简单的倒排索引表。在资料表里增加keywords字段,存标题和描述里提取的关键词,用空格分隔。搜索时改查keywords LIKE '%keyword%',因为keywords字段比较短,扫描成本远低于直接查完整标题和描述。在实际项目中,我用HanLP这个轻量大分词工具在后台定时提取关键词并同步到keywords字段里。HanLP支持多个版本,如果不想引入较大的依赖和模型,用最简单的StandardTokenizer静态调用也能达到不错的提取效果。

搜索结果排序上,设计了一个很基础的排序算法:score = 搜索引擎字段匹配权重 + 时间衰减系数。把标题命中记3分、描述命中记1分,关键词命中再额外加分,最后按计算分做ORDER BY。虽然简单,但实测下来比单纯按时间排序的体验要好不少——相关度高的资料能浮到前面。

4.4 高并发下载时的计数处理

下载次数计数看似简单,实则很容易错。如果直接UPDATE resource SET download_count = download_count + 1 WHERE id = ?,在高并发下确实能保证原子性,但每条下载都要2次SQL(查询验证权限+更新计数),热点数据的行锁竞争会让数据库压力剧增。

一个实用方案是把计数操作异步化。下载接口先用Redis的INCR命令做自增,然后返回文件流;定期将Redis中的计数同步到数据库。这样数据库的写压力被削峰填谷,Redis的高性能支撑了频繁的计数更新。如果不想引入Redis,也可以用一个小的定时任务缓冲——把下载事件记录在内存队列中,每30秒批量更新一次数据库,但进程重启可能丢失缓冲区数据,Redis方案更可控。

并发下载时还需要限制用户的重复操作。在Redis里用set命令记录一个“下载中”标识,过期时间设为30秒,防止用户狂点下载按钮产生大量重复请求。这个操作成本极低,但对系统资源保护和用户体验的改善都非常明显。

5. 前端关键模块:路由守卫、文件上传组件与m3u8视频预览

前端工程的核心不只是页面展示,还涉及状态的传递、权限的控制、交互的流畅性。这一节挑出三个关键模块详细讲讲。

5.1 路由守卫与动态侧边栏的权限玩法

Vue Router前置守卫是权限控制的第二道门(第一道是后端接口鉴权)。具体逻辑是:请求路由前先检查本地缓存是否有token。没有token并且目标路由不在白名单内,则强制跳转到登录页。有token但用户信息尚未加载就自动发起一次/user/info请求,获取用户角色和基本信息。

侧边栏菜单的渲染也是根据角色动态生成的。具体思路是在路由配置里给每个需要权限的页面写上meta.role字段,例如管理员的路由meta.role为ROLE_ADMIN,然后根据用户角色过滤路由表,生成侧边栏菜单。这样管理员登录后能看到用户管理和分类管理入口,普通学生登录后只能看到资料列表和个人中心。

有一类很隐蔽的权限漏洞值得注意:不能只做路由守卫,因为路由守卫只是前端层面的隐性控制,用户手动请求后端的接口地址时,后端如果没做校验一样会泄露数据。所以角色的权限校验必须前后端共同完成:前端隐藏入口、后端拦截非法请求。

5.2 文件上传组件的封装与进度展示

文件上传在多个页面都要用到:教师端上传资料、个人中心传头像。所以封装成单独的upload组件是很有必要的。基于vue-simple-uploader封装后,对外暴露的props包括文件类型限制、上传地址、分片大小、最大文件数等。

大文件上传不能只给一个"上传中"的菊花转,要给出百分比进度。vue-simple-uploader的进度事件处理起来很自然:监听file对象的progress事件,实时更新进度条数值。设计了一个上传队列面板,显示所有待上传文件和各自的上传速度、进度、状态,能直观看到哪个文件传完了、哪个失败了、失败原因是什么。

断点续传的实现机制值得展开:前端在调用初始化接口时,如果后端返回文件已存在部分分片,响应中会包含已上传完成的索引列表。前端根据这个列表自动跳过已提交过的分片,只上传缺失的部分,这就是续传的本质。实测在校园网环境下,从普通上传升级为分片上传后,一份700MB的视频从经常失败变成稳定成功,这个改造的核心价值说得非常清楚。

5.3 在线预览与m3u8视频点播

资料平台不只是下载,在线预览能极大提升学生的使用体验。PDF和图片的预览较易实现,直接用浏览器内置能力或者iframe嵌入即可。比较麻烦的是视频预览,尤其是某些教师上传的格式为m3u8的流媒体视频。

Vue播放m3u8在社区里有一个很成熟的做法:使用video.js配合videojs-contrib-hls插件,或者用hls.js。我在项目中选了hls.js,原因是它对hls的支持非常标准。需要特别注意的是,hls.js需要通过Hls.isSupported()判断当前浏览器是否支持,不支持的降级为video标签的native播放,处理代码时把这个兼容逻辑考虑进去。

// m3u8播放的核心逻辑简化示例 if (Hls.isSupported()) { const hls = new Hls(); hls.loadSource(videoUrl); hls.attachMedia(videoElement); hls.on(Hls.Events.MANIFEST_PARSED, () => { videoElement.play(); }); } else if (videoElement.canPlayType('application/vnd.apple.mpegurl')) { // Safari原生支持 videoElement.src = videoUrl; }

只有图片、PDF在线预览够用,但视频这个高频需求做好之后,平台的完整性提升非常明显。

6. 部署上线的完整流程与踩坑记录

源码能跑通只是走了一半,真正把一个项目送到服务器供几十个学生同时使用,会遇到一系列部署层面的问题。这里把从零到上线的完整流程写清楚,包括一些折腾过的大坑。

6.1 本地开发环境的搭建

在本地跑通项目,建议先安装这几样东西:JDK 8或11、Maven 3.6+、Node.js 14+、MySQL 8.0。IDE个人推荐IDEA,社区版也能胜任前后端两套代码的开发。如果IDEA创建SpringBoot项目时下载依赖超时,解决方式是更换Maven镜像源为阿里云镜像,在settings.xml里修改mirror节点配置即可,改动很小但效果立竿见影。

MySQL安装完成后,需要新建数据库并导入项目中的sql脚本。脚本文件里包含了建表语句和初始数据,包括默认管理员账号admin/admin123(密码经过BCrypt加密,不需要手动改数据库,登录后可在个人中心修改)。如果执行建表脚本时报错,通常是字符集和时区的问题,连接时指定下characterEncoding=utf8serverTimezone=Asia/Shanghai就能解决。

后端的application.yml配置需要根据自己的环境改数据库账号密码。前端工程启动前,先执行npm install安装依赖,过程中如果依赖安装很慢,用npm config set registry https://registry.npmmirror.com切换到国内镜像。然后启动后端,再执行npm run serve启动前端开发服务器,浏览器访问http://localhost:8080即可看到首页。

6.2 前后端分离部署方案对比

部署到云服务器时,有两种方案可以选择。

第一种是传统部署:后端打包成jar包用java -jar运行,前端npm run build之后把dist目录放到Nginx的html目录。Nginx既负责静态资源的托管,也负责接口反向代理。这是最常见的方案,简单直观。

第二种是Docker容器化:写一个docker-compose.yml编排MySQL、后端服务、前端Nginx三个容器。好处是环境隔离,换服务器迁移部署很方便。坏处是增加了学习成本,还要处理容器与宿主机之间的数据卷挂载问题,比如MySQL的数据目录如果不挂载到宿主机,容器删了数据就全丢了。

校园项目如果没有特殊的快速扩缩容要求,用第一种传统部署方案就够了,运维成本最低。Docker容器化的价值在集群场景下才会充分体现出来,作为个人项目简单跑起来第一优先。

6.3 阿里云服务器的完整配置

阿里云是很多学生项目第一台服务器的首选。服务器选配时,校园项目2核4G配置够用,带宽选3Mbps以上,系统镜像选Ubuntu 20.04或CentOS 7.9。

一个很多人一开始就卡住的地方:安全组规则。云服务器默认只开放22端口,其他端口全封闭。需要在安全组里手动放行3306端口(如果不用远程连接可以不开放,减少暴露面)、8080端口或80端口。更安全的做法是3306端口完全不开,数据库只在服务器本地使用,远程管理用SSH终端来执行SQL操作,这样能减少不少被攻击的风险。

后端服务启动时用nohup java -jar campus-share-0.0.1-SNAPSHOT.jar > app.log 2>&1 &,日志输出到app.log方便排查。如果服务器一重启服务就没了,可以写一个systemd服务脚本,把jar包托管成系统服务,设置Restart=always开机自启。

Nginx的配置是整个部署环节中最关键的部分。前端静态资源交给Nginx托管,接口路径/api/反向代理到后端端口,核心配置如下。

server { listen 80; server_name your-domain.com; # 前端静态资源 root /var/www/campus-share/dist; index index.html; # 前端路由history模式需要重定向到index.html location / { try_files $uri $uri/ /index.html; } # 后端接口反向代理 location /api/ { proxy_pass http://127.0.0.1:8081; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # 上传文件访问 location /uploads/ { alias /var/www/campus-share/uploads/; } }

这个配置中有两个容易踩的大坑。第一个是Vue Router如果使用history模式,刷新非首页页面时Nginx会报404,必须用try_files配置兜底重定向到index.html。第二个是反向代理时如果没设client_max_body_size,大文件上传会被Nginx拒绝返回413错误,需要在server块里加client_max_body_size 1024m;放开这个限制。

6.4 上线后观察容量和稳定性

线上运行后要重点观察两个指标:磁盘空间和内存占用。上传文件越来越多时,磁盘告急是必然发生的事。建议在云服务器控制台配置磁盘告警,空闲率低于20%就提醒,及时做清理或扩容。

另一个稳定性的隐患是文件上传目录的权限问题。Nginx对/uploads/目录的访问需要正确的读写权限,后端创建目录时如果使用的是mkdir -p,那么目录的所有者和权限取决于执行用户的umask。推荐在部署文档里明确指定uploads目录的创建命令:sudo mkdir -p /var/www/campus-share/uploads && sudo chown -R www-data:www-data /var/www/campus-share/,避免因权限问题导致文件写入失败。

7. 从课程设计到生产级系统,还需要做哪些升级

如果你不满足于"能跑起来",想让这套系统更有竞争力,可以从下面几个方向继续扩展。

数据可视化看板是一个值得投入的方向。当前系统的管理后台只提供了最简单的数据列表,如果加上ECharts图表,展示每日上传量、下载量TOP10、活跃用户分布等,整个管理端立刻会显得专业很多。实现上可以通过定时任务在每天凌晨统计数据,存到一张summary表中,前端直接查汇总数据渲染图表,避免实时聚合大表。

消息通知机制也是一个常见的需求。学生上传资料后,管理员审核通过与否需要通知上传者;管理员操作封禁账号后,需要通知被处理的用户。实现不一定要引入RabbitMQ这些消息队列,最简方案是做一个通知表,业务操作时往通知表插入一条记录,用户登录时前端轮询接口拉取未读消息即可。数据量大了之后再考虑WebSocket实时推送。

文件块的合并和上传目前是同步处理的,合并大文件时会长时间占用HTTP连接。生产级方案是用线程池异步合并,接口返回"正在合并"的状态,前端轮询查询合并结果。这需要在前端配合设计上传状态机,复杂度会增加不少,但系统健壮性会有显著提升。

全文搜索如果资料量真的超过了十万条,就值得认真考虑引入ElasticSearch了。当MySQL的keywords LIKE查询超过200ms时,搜索体验就会有明显卡顿,此时可以设计一个同步任务,把资料表的数据同步到ES索引中,搜索接口改成先查ES再取详情。这个升级可以以后按需做,前期能用MySQL先顶一阵。

8. 疑难杂症排查实录

整个开发部署过程中最费心力的往往不是写代码,而是排错。这里整理几个真实遇到且极具代表性的问题,如果你不幸踩中,可以直接对照排查。

现象一:接口报401,但登录接口明明正常

排查链路:先看浏览器控制台请求头,确认请求带了Authorization。如果带了,看是token过期、格式错误,还是后端解析失败。一个容易忽略的点是JWT工具类解析时如果签名密钥与生成时不一致,会直接抛出异常,需要在捕获异常时返回401而不是500。

现象二:前端页面能打开,但所有接口都404

排查链路:先确认后端是否启动了,再确认Nginx的proxy_pass地址是否正确。如果后端地址是localhost:8081而Nginx在宿主机上,那么proxy_pass必须写http://127.0.0.1:8081,不能写localhost:8081——在某些系统上,Nginx解析localhost可能会优先解析到IPv6地址,导致连接不到应用。另一个原因可能是跨域问题,Vue开发环境访问8081接口时如果后端没配CORS,浏览器会拦截,控制台会明确提示跨域错误。

现象三:上传大文件到99%后失败

这个问题的典型原因就是Nginx默认允许的请求体太小。client_max_body_size默认值为1m,上传几MB的图片没问题,遇到几百MB的视频就必然会失败。解决方式就是前面提到的在server块里调大该参数。

现象四:生产环境刷新页面404

这个在部署章节出现过:Vue Router的history模式需要Nginx配合try_files $uri $uri/ /index.html;。如果你的前端路由用的是hash模式则不存在这个问题,但URL会多一个#号,不够美观。推荐用history模式并正确配置Nginx。

这些排查经验,其实不掌握也很正常,都是靠翻日志、搜报错、对比官方文档一步步试出来的。部署一个前后端分离项目,最大的价值就在于能让你真正理解客户端、服务端、数据库、代理这几层之间的协作关系,出错时有一种"全链路视角"的排错能力。

我个人在实际操作中的体会是:不要一开始就追求用上最新最强的框架,先把这套基础的校园资料分享平台跑通,然后把一个个模块吃透,比徒有大量依赖却说不清原理要有价值得多。遇到报错先看日志,日志里没有线索就去确认环境配置,一步步缩小范围,绝大多数问题都能定位到具体哪一层。最后再分享一个小技巧:在本地写代码时养成用IDEA的MyBatis Log插件输出完整可执行SQL的习惯,排查MyBatis相关的数据问题会省很多时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询