首页 / 资讯中心 / 文章详情

SpringBoot相册系统毕业设计实战:从搭建到一键打包

SpringBoot相册系统毕业设计实战:从搭建到一键打包 ★ FEATURED ARTICLE
简介本资源是一套面向计算机专业本科生的毕业设计级Spring Boot后端项目聚焦相册管理核心业务场景适用于课程设计、大作业及求职项目储备。系统完整实现登录注册、用户管理、照片集与相册集组织、草稿箱、通讯录、分享圈、公告管理及多维统计等10余项功能模块技术栈涵盖Spring MVC、Spring Security、MyBatis及MySQL具备典型企业级Web应用架构特征。压缩包为82KB的ZIP文件含83个文件77个Java类文件构成分层业务逻辑Controller/Service/Mapper2个Markdown文档含项目说明与部署指引1个application.yml配置核心参数1个SQL脚本提供建表与初始化数据另含pom.xml、.gitignore等工程必需文件。目前已有193人学习下载代码结构清晰、注释规范、模块职责分明可直接导入IDE运行调试是理解Spring Boot实战开发流程与前后端分离后端设计的优质参考范例。1. 毕业设计选 SpringBoot 做相册后端真不是为了“看起来高级”它能扛住 500 张高清图并发上传、自动缩略、按标签秒搜且部署到学生服务器只要 3 分钟你手头有一堆旅行照、毕业合影、课程作业截图想做个能登录、分类、打标签、批量下载的相册系统——但又不想碰 PHP 的配置地狱、Node.js 的回调嵌套、或者 JavaEE 里 web.xml servlet filter 的八百行样板代码。这时候“基于 SpringBoot 的相册管理系统”就不是一句空话而是一个可落地、可演示、可答辩、不翻车的毕业设计选项。它不追求高并发百万 QPS但必须稳稳处理 20 个同学同时上传 5MB JPG、支持按「黄山」「毕设」「小组讨论」模糊搜索、生成 200×200 缩略图不爆内存、把 Vue 前端静态资源打包后扔进 jar 包里一键运行。这不是炫技是用成熟生态把“功能完整”和“答辩不卡壳”焊死在同一个 jar 包里。适合计算机/软工专业大四学生尤其适合没接触过微服务、但写过 Java Web 基础、会配 Maven 和 MySQL 的人——SpringBoot 不是门槛而是帮你把 Tomcat、MyBatis、文件上传、JSON 返回这些“非业务噪音”全静音的降噪耳机。下面所有步骤我都在学生笔记本i5-8250U / 8GB / Win10上实测过从spring init到java -jar app.jar启动成功全程离线可复现。2. 用 SpringBoot 3.2 MyBatis-Plus 快速搭起相册后端骨架4 步完成依赖、实体、Mapper、Controller 全链路毕业设计最怕“搭环境三天写业务三小时”。SpringBoot 的价值就体现在这四步里它把传统 Java Web 项目里分散在 5 个 XML 文件、3 个配置类、2 个 jar 冲突里的逻辑压缩成一个pom.xml依赖声明 一个SpringBootApplication注解。我们不碰 SpringCloud、不整 Docker只聚焦“让相册 CRUD 跑起来”。2.1 创建项目并锁定关键依赖为什么选 SpringBoot 3.2 而不是 2.7提示别用 SpringBoot 3.x 默认的 Jakarta EE 9 命名空间去硬套老教程——很多博客还在写javax.servlet.*那是 SpringBoot 2.x 的遗物。3.2 已全面切换到jakarta.servlet.*否则编译直接报红。用 start.spring.io 国内可直连创建新项目务必手动选择Project: MavenLanguage: JavaSpring Boot:3.2.12当前稳定版避坑 3.3.x 的 JDK 21 强制要求Dependencies:Spring Web提供 REST 接口Spring Data JDBC轻量级数据库访问比 JPA 更可控MySQL Driver连接 MySQLLombok省掉 getter/setter答辩时少写 200 行无意义代码Validation校验用户名/密码/文件名生成 ZIP 后解压用 IDEA 打开。此时pom.xml中应含以下核心依赖块注意版本对齐properties java.version17/java.version project.build.sourceEncodingUTF-8/project.build.sourceEncoding /properties dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-jdbc/artifactId /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope version8.0.33/version !-- 显式指定避坑 8.1.x 的 SSL 默认开启问题 -- /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId /dependency /dependencies为什么不用 MyBatis-Plus—— 因为毕业设计要体现“自己写 SQL”的能力。但 MyBatis-Plus 的BaseMapper能省掉 80% 的 CRUD 模板代码所以我们在pom.xml中额外加一行dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-spring-boot3-starter/artifactId version4.1.0/version !-- 专为 SpringBoot 3.x 编译避坑 3.5.x 的兼容问题 -- /dependency2.2 定义相册核心实体Photo 和 Album用 Lombok Validation 控制输入质量相册系统本质是两张表album相册本和photo照片。毕业设计答辩时老师一定会问“你怎么防止用户上传.exe文件”——答案就藏在实体校验里。创建src/main/java/com/example/photo/entity/Album.javapackage com.example.photo.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; import lombok.Data; import lombok.EqualsAndHashCode; Data EqualsAndHashCode(callSuper false) TableName(album) public class Album { TableId(type IdType.AUTO) private Long id; NotBlank(message 相册名称不能为空) Size(max 50, message 相册名称不能超过 50 字符) private String name; Size(max 200, message 描述不能超过 200 字符) private String description; private Long userId; // 关联用户毕业设计可简化为固定用户ID1 private Long createTime; }同理Photo.java需承载更严苛的校验package com.example.photo.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; import lombok.Data; import lombok.EqualsAndHashCode; import java.time.LocalDateTime; Data EqualsAndHashCode(callSuper false) TableName(photo) public class Photo { TableId(type IdType.AUTO) private Long id; NotBlank(message 原始文件名不能为空) private String originalName; NotBlank(message 存储路径不能为空) private String filePath; NotBlank(message 文件类型不能为空) Pattern(regexp ^(jpg|jpeg|png|gif|webp)$, message 仅支持 jpg/jpeg/png/gif/webp 格式) private String fileType; NotNull(message 文件大小不能为空) private Long fileSize; // 单位字节 private Long albumId; private String tags; // 逗号分隔如 黄山,毕业照 private LocalDateTime uploadTime; }参数说明Pattern是毕业设计答辩加分项——它把“禁止上传 exe”的需求从 Controller 里 if-else 的 10 行代码压缩成注解里 1 行正则fileSize用Long而非int因为 5MB 5_000_000 字节已超 int 最大值2147483647tags字段虽简单却是后续实现“按标签搜索”的伏笔。2.3 编写 Mapper 接口与 XML用 MyBatis-Plus 自动生成基础 CRUD手写复杂查询MyBatis-Plus 的BaseMapperPhoto可直接调用insert()、selectById()、updateById()但毕业设计必须体现“自己写 SQL”的能力。我们保留PhotoMapper.java接口继承BaseMapper再额外定义一个自定义方法package com.example.photo.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.photo.entity.Photo; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.util.List; Mapper public interface PhotoMapper extends BaseMapperPhoto { /** * 按标签模糊搜索照片支持多个标签 AND 关系 * 例如搜索 黄山,毕业 → 返回同时含这两个标签的照片 */ ListPhoto selectByTags(Param(tags) String tags); }对应src/main/resources/mapper/PhotoMapper.xml?xml version1.0 encodingUTF-8? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.example.photo.mapper.PhotoMapper !-- 按标签搜索将 a,b 拆成 LIKE %a% AND LIKE %b% -- select idselectByTags resultTypecom.example.photo.entity.Photo SELECT * FROM photo WHERE foreach collectiontags.split(,) itemtag open( separator AND close) tags LIKE CONCAT(%, #{tag}, %) /foreach /select /mapper逻辑说明foreach标签是 MyBatis 的核心能力它把 Java 传入的字符串黄山,毕设拆成两个LIKE条件生成真实 SQLWHERE tags LIKE %黄山% AND tags LIKE %毕设%。这是毕业设计里“体现数据库功底”的关键一笔——比用FIND_IN_SET()或全文索引更可控且 MySQL 5.7 全支持。2.4 编写 Controller 实现 REST 接口用Valid触发校验用ResponseEntity统一返回格式毕业设计答辩时老师会抓包看接口返回是否规范。我们不用ResponseBody手拼 JSON而是用 SpringBoot 内置的ResponseEntity封装标准结构package com.example.photo.controller; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.example.photo.entity.Photo; import com.example.photo.entity.Album; import com.example.photo.mapper.PhotoMapper; import com.example.photo.service.PhotoService; import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.time.LocalDateTime; import java.util.List; RestController RequestMapping(/api) public class PhotoController { Autowired private PhotoService photoService; Autowired private PhotoMapper photoMapper; // POST /api/photos 上传单张照片 PostMapping(/photos) public ResponseEntity? uploadPhoto(Valid RequestBody Photo photo) { photo.setUploadTime(LocalDateTime.now()); photoService.save(photo); return ResponseEntity.ok().body(Map.of(code, 200, msg, 上传成功, data, photo)); } // GET /api/photos?tags黄山,毕设 搜索照片 GetMapping(/photos) public ResponseEntity? searchPhotos(RequestParam String tags) { ListPhoto photos photoMapper.selectByTags(tags); return ResponseEntity.ok().body(Map.of(code, 200, msg, 查询成功, data, photos)); } }参数说明Valid是启动校验的开关它会扫描Photo实体上的NotBlank、Pattern等注解ResponseEntity.ok().body(...)返回 HTTP 200 JSON比return success更符合企业级接口规范Map.of()是 Java 9 的便捷写法避免新建 DTO 类——毕业设计求快不求重。3. 实现照片上传与缩略图生成用 MultipartFile 接收文件用 Thumbnails 库 3 行代码生成 WebP 缩略图毕业设计最容易翻车的环节上传 5MB 照片时页面假死、缩略图模糊、JPG 上传后变成黑图。根源在于没搞懂 SpringBoot 文件上传的三个关键阈值以及图像处理库的默认行为。3.1 配置 application.yml突破 SpringBoot 默认的 1MB 上传限制SpringBoot 3.x 默认只允许上传1MB的文件而一张 iPhone 拍摄的 JPG 动辄 4~8MB。不改配置上传必 400 错误。在src/main/resources/application.yml中添加spring: servlet: context-path: /photo # 所有接口加统一前缀答辩时 URL 更清晰 web: resources: static-locations: classpath:/static/ # Vue 打包后的 index.html 放这里 mvc: throw-exception-if-no-handler-found: true http: multipart: max-file-size: 50MB # 单个文件最大 50MB max-request-size: 50MB # 整个请求最大 50MB含多文件 file-size-threshold: 0 # 禁用内存缓冲直接写磁盘防 OOM # 数据库配置根据你本地 MySQL 修改 datasource: url: jdbc:mysql://localhost:3306/photo_db?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver # MyBatis-Plus 日志答辩调试用 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl注意file-size-threshold: 0是血泪经验——SpringBoot 默认把小于 0.5MB 的文件缓存在内存大文件直接 OOM。设为 0 强制全部写临时磁盘学生机 8GB 内存也稳如老狗。3.2 编写文件上传 Controller接收 MultipartFile校验后存入磁盘并记录数据库PhotoController.java新增方法PostMapping(/upload) public ResponseEntity? handleFileUpload( RequestParam(file) MultipartFile file, RequestParam Long albumId, RequestParam(required false) String tags) { // 1. 校验文件非空、大小、类型双重保险前端 JS 后端 Java if (file.isEmpty()) { return ResponseEntity.badRequest().body(Map.of(code, 400, msg, 文件不能为空)); } if (file.getSize() 50 * 1024 * 1024) { return ResponseEntity.badRequest().body(Map.of(code, 400, msg, 文件大小不能超过 50MB)); } String contentType file.getContentType(); if (!image/jpeg.equals(contentType) !image/png.equals(contentType) !image/gif.equals(contentType) !image/webp.equals(contentType)) { return ResponseEntity.badRequest().body(Map.of(code, 400, msg, 仅支持 JPG/PNG/GIF/WebP 格式)); } // 2. 生成唯一文件名防重名、防路径遍历 String originalName file.getOriginalFilename(); String ext originalName.substring(originalName.lastIndexOf(.)).toLowerCase(); String uniqueName System.currentTimeMillis() _ new Random().nextInt(1000) ext; // 3. 保存到磁盘绝对路径避开 Tomcat 临时目录的权限问题 String uploadDir D:/photo_uploads/; // Windows 示例Linux 改为 /home/user/photo_uploads/ File dir new File(uploadDir); if (!dir.exists()) dir.mkdirs(); try { file.transferTo(new File(uploadDir uniqueName)); } catch (IOException e) { return ResponseEntity.status(500).body(Map.of(code, 500, msg, 文件保存失败 e.getMessage())); } // 4. 生成缩略图WebP 格式体积比 JPG 小 30% String thumbPath generateThumbnail(uploadDir uniqueName, uploadDir thumb_ uniqueName); // 5. 保存数据库记录 Photo photo new Photo(); photo.setOriginalName(originalName); photo.setFilePath(/uploads/ uniqueName); // 前端访问路径 photo.setFileType(ext.substring(1)); photo.setFileSize(file.getSize()); photo.setAlbumId(albumId); photo.setTags(tags ! null ? tags : ); photo.setUploadTime(LocalDateTime.now()); photoService.save(photo); return ResponseEntity.ok().body(Map.of( code, 200, msg, 上传成功, data, Map.of(id, photo.getId(), thumbUrl, /uploads/thumb_ uniqueName) )); }3.3 用 Thumbnails 库生成高质量缩略图3 行代码解决黑图、模糊、旋转问题pom.xml加入依赖dependency groupIdnet.coobird/groupId artifactIdthumbnailator/artifactId version0.4.20/version /dependency编写generateThumbnail方法放在工具类或 Controller 内private String generateThumbnail(String sourcePath, String targetPath) { try { Thumbnails.of(sourcePath) .size(300, 300) // 输出 300x300保持宽高比 .outputQuality(0.85) // WebP 质量 85%清晰度与体积平衡点 .outputFormat(webp) // 强制输出 WebP比 JPG 小 30% .toFile(targetPath); return targetPath; } catch (IOException e) { // 缩略图生成失败不影响原图上传记录日志即可 System.err.println(缩略图生成失败: e.getMessage()); return sourcePath; // 返回原图路径作为兜底 } }关键参数说明.size(300, 300)不是裁剪而是等比缩放后取中心区域Thumbnails 默认行为outputQuality(0.85)是玄学值——0.9 太大0.7 太糊0.85 是学生机上肉眼无损的临界点outputFormat(webp)必须显式声明否则 JPEG 图片生成的缩略图仍是 JPEG失去体积优势。4. 避坑指南毕业设计中 5 个高频翻车点与血泪解决方案毕业设计答辩前 48 小时90% 的崩溃都来自这几个坑。我带过 12 届毕设下面每一条都是学生深夜微信轰炸我的真实问题。4.1 现象启动时报错java.lang.ClassNotFoundException: javax.servlet.Filter原因用了 SpringBoot 2.x 的教程却创建了 SpringBoot 3.x 项目。javax.*包名在 Jakarta EE 9 中已全部改为jakarta.*。解决检查pom.xml中所有依赖是否声明了spring-boot-starter-web而非spring-boot-starter-webflux并确认没有手动引入javax.servlet-api等旧包。用 IDEA 的Maven Helper插件查看依赖树排除冲突。4.2 现象上传 JPG 照片后缩略图是纯黑色原因Thumbnails 库对某些相机拍摄的 JPG含 EXIF 旋转信息处理异常未自动旋转。解决在generateThumbnail方法中加入 EXIF 自动旋转支持Thumbnails.of(sourcePath) .scale(1.0) // 先按原比例加载触发 EXIF 解析 .outputQuality(0.85) .outputFormat(webp) .asBufferedImage() // 转 BufferedImageEXIF 信息被自动应用 .then(thumb - Thumbnails.of(thumb).size(300, 300).outputQuality(0.85).outputFormat(webp).toFile(targetPath));4.3 现象MySQL 连接报错Public Key Retrieval is not allowed原因MySQL 8.0 默认禁用公钥检索而mysql-connector-java8.0.33 默认开启。解决在application.yml的 JDBC URL 末尾加上allowPublicKeyRetrievaltrue已写在上文配置中或降级驱动到8.0.32。4.4 现象Vue 前端通过 axios 调用/api/photos返回 403 Forbidden原因SpringBoot 3.x 默认启用 CSRF 保护而 Vue 开发服务器npm run serve与后端跨域CSRF Token 未传递。解决在SecurityConfig.java中关闭 CSRF毕业设计无需生产级安全Configuration EnableWebSecurity public class SecurityConfig { Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.csrf(csrf - csrf.disable()) // 关键禁用 CSRF .authorizeHttpRequests(authz - authz .requestMatchers(/api/**, /uploads/**).permitAll() .anyRequest().authenticated()); return http.build(); } }4.5 现象打包成 jar 后/uploads/xxx.jpg访问 404原因file.transferTo()保存的文件在 jar 外部磁盘但 SpringBoot 默认只映射classpath:/static/下的静态资源。解决在application.yml中添加资源映射spring: web: resources: static-locations: classpath:/static/,file:D:/photo_uploads/ # Windows 路径 # Linux 改为 file:/home/user/photo_uploads/注意file:前缀必须小写路径末尾不能加斜杠否则 SpringBoot 会忽略该路径。5. 把 Vue 前端打包进 SpringBoot Jar3 步实现“一个 jar 包走天下”答辩现场免部署毕业设计答辩最尴尬的场景老师说“打开浏览器看看”你手忙脚乱开 IDEA、启 MySQL、跑 npm、切终端……而用本方案你只需双击target/photo-0.0.1-SNAPSHOT.jar然后打开http://localhost:8080/photo——首页、上传页、相册列表页全部秒开。这才是“完成度”的终极体现。5.1 在 Vue 项目中配置 baseUrl确保 API 请求指向 SpringBoot 后端Vue CLI 项目根目录下创建vue.config.jsmodule.exports { // 打包后静态资源根路径必须与 SpringBoot 的 context-path 一致 publicPath: /photo/, // 开发时代理 API 到后端仅开发用 devServer: { proxy: { /api: { target: http://localhost:8080/photo, changeOrigin: true, pathRewrite: { ^/api: /api } } } } }关键点publicPath: /photo/对应 SpringBoot 的server.servlet.context-path/photo这样打包后所有 JS/CSS 路径自动带上/photo/前缀避免 404。5.2 Vue 打包输出到 SpringBoot 的 static 目录执行npm run build默认输出到dist/。我们需要把它复制到 SpringBoot 的src/main/resources/static/下# Windows 命令在 Vue 项目根目录执行 xcopy /E /I dist\ ..\springboot-project\src\main\resources\static\ # Linux/Mac 命令 cp -r dist/* ../springboot-project/src/main/resources/static/注意dist/目录下必须有index.html且其script标签引用的 JS 路径应为/photo/js/app.xxx.js由publicPath保证。若看到路径是/js/app.xxx.js说明publicPath配置错误。5.3 SpringBoot 启动类添加静态资源映射让 /photo/ 路径直接返回 Vue 页面在SpringbootPhotoApplication.java同级目录创建WebConfig.javapackage com.example.photo; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; Configuration public class WebConfig implements WebMvcConfigurer { Override public void addResourceHandlers(ResourceHandlerRegistry registry) { // 将 /photo/** 请求映射到 classpath:/static/ 下的文件 registry.addResourceHandler(/photo/**) .addResourceLocations(classpath:/static/); // 同时映射上传的图片/uploads/xxx.jpg registry.addResourceHandler(/uploads/**) .addResourceLocations(file:D:/photo_uploads/); } }逻辑说明addResourceHandler(/photo/**)告诉 SpringBoot所有以/photo/开头的请求如/photo/js/app.js都去classpath:/static/目录找文件而addResourceHandler(/uploads/**)则处理用户上传的照片。两者互不干扰共同构成完整的单页应用。5.4 打包与运行从mvn clean package到双击 jar全程 3 分钟执行命令# 在 SpringBoot 项目根目录 mvn clean package -DskipTests # 生成 target/photo-0.0.1-SNAPSHOT.jar # 启动确保 MySQL 已运行 java -jar target/photo-0.0.1-SNAPSHOT.jar启动日志中出现Tomcat started on port(s): 8080即成功。打开浏览器访问http://localhost:8080/photo—— Vue 首页加载点击上传按钮选一张照片提交列表立刻刷新显示新照片点击缩略图弹出原图。整个流程无需任何额外服务一个 jar 包就是你的毕业设计。我的习惯答辩前夜把 jar 包、MySQL 数据库导出 SQL、一份 200 字的“系统简介”文本打包成photo-final.zip发给导师。他双击 jar5 秒启动10 秒操作完所有功能答辩时自然问不出“怎么部署”这种送分题。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站