ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

JavaWeb电子相册毕设源码:Servlet+JSP+MySQL实现上传分页缩略图

JavaWeb电子相册毕设源码:Servlet+JSP+MySQL实现上传分页缩略图 简介这是一套基于JavaWeb的电子相册与网络相册管理系统面向计算机相关专业正在做毕设的学生以及需要项目实战练习的Java学习者可直接作为毕业设计选题使用。项目采用B/S结构后台以JSP、Servlet、JDBC为核心技术MySQL作为数据库开发环境涉及JDK、Eclipse与Tomcat适合掌握Java基础语法、希望深入理解Web开发流程的中级学习者。资源包共3个文件包含1个zip项目源码包、1个sql数据库脚本和1个txt项目说明整体约17.28MB源码与建库脚本配套齐全导入后即可运行调试。系统分为前台用户界面与后台管理两大模块前台实现用户注册、网站介绍、站场动态、用户相册、空间共享与在线交流后台涵盖成员管理、公告管理、网络图像管理、照片管理、相册管理、管理员信息管理及站场动态等功能功能完善、界面美观、操作简单。目前已有1303人学习下载可作为完整赛题方案参考帮助读者快速理清JavaWeb项目的分层结构、数据库表设计与前后台交互逻辑节省从零搭建的时间成本。1. 从一份能跑起来的 JavaWeb 电子相册源码说起很多计算机专业同学做毕设时最头疼的不是写代码而是从零搭一个能演示、能答辩、能交差的完整系统。电子相册这个题目看起来简单——上传图片、分类、浏览、删除但真动手就会发现图片存哪、数据库怎么设计、分页怎么做、权限怎么控、前端怎么展示缩略图每一个都是坑。基于 JavaWeb 的电子相册本质是一个典型的 Servlet JSP MySQL 三层架构 Web 应用它覆盖了 JavaWeb 课程里几乎全部核心知识点请求响应、会话管理、文件上传、JDBC 操作、MVC 分层。对本科毕设来说它体量适中、技术栈经典、答辩时老师一听就懂而且源码和数据库脚本都能自己掌控不依赖任何云服务。这篇笔记就按拿到一份源码后怎么跑通、怎么改、怎么避坑的顺序把整套落地路径讲清楚。2. 电子相册的技术选型为什么 ServletJSPMySQL 仍是毕设稳妥解2.1 三层架构在相册场景里的具体分工电子相册这个业务数据流其实很清晰用户在前端页面点击上传浏览器把图片文件和表单字段一起 POST 到服务器服务器端接收文件、生成存储路径、把元数据文件名、分类、上传时间、所属用户写进数据库浏览时再从数据库查出记录拼出图片 URL 返回给页面渲染。这套流程天然适合 MVC 分层。具体到代码结构我一般会这样划分dao层只负责和 MySQL 打交道一个PhotoDao类里放insertPhoto、listByCategory、deleteById这些方法service层做业务判断比如文件类型校验、大小限制、分类是否存在servlet层做请求分发和参数解析一个PhotoServlet用action参数区分 upload/list/deletejsp只做展示不写 Java 逻辑。这样分层的好处是答辩时老师问你的业务逻辑写在哪你能明确指出来而不是所有代码堆在一个 JSP 里。为什么不用 SpringBoot不是不能用而是毕设场景下 SpringBoot 的自动配置反而让很多同学说不清底层原理。老师一问你的请求是怎么到 Controller 的如果只会说注解就容易翻车。Servlet 虽然老但每一个环节都是显式的web.xml或WebServlet里配置得明明白白答辩时反而好讲。当然如果你时间充裕、想加分用 SpringBoot 重写一遍也可以但核心的数据库设计和文件上传逻辑是一样的。2.2 数据库表设计三张表就够别过度设计电子相册的数据库脚本很多同学一上来就设计七八张表结果自己都理不清关联。我的血泪经验是毕设阶段三张表足够——用户表、相册分类表、照片表。-- 用户表只存最基础的登录信息 CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, -- 存 MD5 后的值别存明文 create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 分类表每个用户可以有多个相册分类 CREATE TABLE category ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, name VARCHAR(50) NOT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_user (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 照片表核心表存文件路径而不是文件本身 CREATE TABLE photo ( id INT PRIMARY KEY AUTO_INCREMENT, category_id INT NOT NULL, user_id INT NOT NULL, file_name VARCHAR(255) NOT NULL, -- 原始文件名展示用 file_path VARCHAR(500) NOT NULL, -- 服务器相对路径读取用 file_size BIGINT DEFAULT 0, upload_time DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_category (category_id), INDEX idx_user (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里的关键决策是数据库只存路径不存二进制。有些同学图省事把图片用 BLOB 存进 MySQL结果数据库体积暴涨、查询变慢、备份困难答辩时被问为什么不用文件系统就答不上来。正确做法是图片存服务器磁盘目录比如webapp/upload/数据库只记录相对路径。file_path字段长度给到 500 是留余量因为分类目录可能嵌套。字符集统一用utf8mb4避免中文文件名乱码——这个坑后面还会细说。2.3 环境准备与项目导入的最小步骤拿到源码和数据库脚本后第一步不是急着跑而是把环境对齐。常见组合是 JDK 8 或 11、Tomcat 8.5/9、MySQL 5.7/8.0、IDEA。版本不要追新Tomcat 10 把javax.servlet改成了jakarta.servlet老源码直接跑会报ClassNotFoundException这是新手最容易翻车的地方。# 1. 导入数据库脚本假设脚本名为 album.sql mysql -u root -p -e CREATE DATABASE album_db DEFAULT CHARSET utf8mb4; mysql -u root -p album_db album.sql # 2. 确认数据库连接信息修改源码里的 db.properties # 通常长这样 # jdbc.urljdbc:mysql://localhost:3306/album_db?useUnicodetruecharacterEncodingutf8mb4serverTimezoneAsia/Shanghai # jdbc.usernameroot # jdbc.password你的密码导入脚本时注意如果脚本里已经包含CREATE DATABASE和USE语句就直接mysql -u root -p album.sql如果没有就要先建库再导入。连接串里的serverTimezone必须加MySQL 8.0 不加会报时区错误。characterEncoding用utf8mb4而不是utf8否则 emoji 和部分生僻字会丢。改完配置后在 IDEA 里配置 Tomcat 运行访问http://localhost:8080/项目名/看首页是否出来。如果 404先检查web.xml里的welcome-file和实际首页文件名是否一致。3. 核心功能落地上传、分页、缩略图三条主线3.1 文件上传Servlet 3.0 的 Part 接口怎么用对文件上传是电子相册的心脏。老教程还在用 Commons FileUpload但 Servlet 3.0 之后标准 API 就够用了少引一个 jar 包少一份麻烦。关键是用MultipartConfig注解声明然后用request.getPart()拿文件。WebServlet(/photo) MultipartConfig( maxFileSize 10 * 1024 * 1024, // 单文件最大 10MB maxRequestSize 50 * 1024 * 1024, // 整个请求最大 50MB fileSizeThreshold 1024 * 1024 // 超过 1MB 写临时文件 ) public class PhotoServlet extends HttpServlet { protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String action req.getParameter(action); if (upload.equals(action)) { Part part req.getPart(file); // 对应表单 namefile String fileName getFileName(part); // 从 header 里解析原始名 // 校验扩展名白名单比黑名单安全 String ext fileName.substring(fileName.lastIndexOf(.) 1).toLowerCase(); if (!Arrays.asList(jpg, jpeg, png, gif).contains(ext)) { resp.getWriter().write({\code\:400,\msg\:\不支持的格式\}); return; } // 用 UUID 重命名避免同名覆盖和中文乱码 String newName UUID.randomUUID().toString() . ext; String saveDir getServletContext().getRealPath(/upload); File dir new File(saveDir); if (!dir.exists()) dir.mkdirs(); part.write(saveDir File.separator newName); // 元数据入库file_path 存相对路径 photoDao.insert(newName, fileName, upload/ newName, part.getSize(), categoryId, userId); } } // Part 的 getSubmittedFileName 在部分容器里返回 null需要从 header 兜底解析 private String getFileName(Part part) { String header part.getHeader(content-disposition); for (String token : header.split(;)) { if (token.trim().startsWith(filename)) { return token.substring(token.indexOf() 2, token.length() - 1); } } return unknown; } }这段代码有几个参数必须说清楚。maxFileSize是单文件上限超过会抛IllegalStateException要在前端也做一次校验否则用户传大图直接 500 体验很差。fileSizeThreshold控制内存缓冲阈值小文件不落临时盘大文件才写磁盘对相册这种小文件居多的场景很合适。重命名用 UUID 是行业惯例既避免同名覆盖又绕开中文文件名在不同操作系统上的编码差异——原始名存在file_name字段里用于展示磁盘上永远是安全的英文名。getSubmittedFileName()在 Tomcat 8 某些版本会返回 null所以用 header 解析兜底更稳。3.2 分页查询别用 LIMIT 拼字符串用 PreparedStatement相册照片一多必须分页。很多同学直接SELECT * FROM photo LIMIT (page-1)*size , size这是 SQL 注入的温床而且页码参数一旦是负数就报错。正确做法是 PreparedStatement 占位同时先查总数算总页数。public ListPhoto listByPage(int userId, int page, int size) { ListPhoto list new ArrayList(); // 先查总数用于计算总页数 String countSql SELECT COUNT(*) FROM photo WHERE user_id ?; // 再查当前页数据ORDER BY 保证顺序稳定 String dataSql SELECT * FROM photo WHERE user_id ? ORDER BY upload_time DESC LIMIT ? OFFSET ?; try (Connection conn DBUtil.getConnection()) { int total; try (PreparedStatement ps conn.prepareStatement(countSql)) { ps.setInt(1, userId); try (ResultSet rs ps.executeQuery()) { rs.next(); total rs.getInt(1); } } try (PreparedStatement ps conn.prepareStatement(dataSql)) { ps.setInt(1, userId); ps.setInt(2, size); ps.setInt(3, (page - 1) * size); // OFFSET 从 0 开始 try (ResultSet rs ps.executeQuery()) { while (rs.next()) { list.add(mapRow(rs)); } } } } catch (SQLException e) { throw new RuntimeException(分页查询失败, e); } return list; }LIMIT ? OFFSET ?的写法比LIMIT ?,?更清晰OFFSET 从 0 开始第 1 页 offset 就是 0。ORDER BY upload_time DESC必须加否则 MySQL 返回顺序不保证翻页时会出现同一张图在两页都出现或都不出现的玄学问题。size建议固定 12 或 15正好被 3 或 4 整除前端网格布局好看。总数查询和分页查询分两条 SQL虽然多一次往返但逻辑清晰毕设场景完全够用。如果非要优化可以用SQL_CALC_FOUND_ROWS但 MySQL 8.0 已不推荐别给自己找麻烦。3.3 缩略图生成Thumbnails 还是手写 ImageIO相册列表页如果直接加载原图一张 5MB 的照片十几张就能把页面拖垮。缩略图是刚需。常见做法有两种引thumbnailator库一行代码搞定或者用 JDK 自带的ImageIO手写缩放。// 方案一thumbnailator简单但多一个依赖 // Thumbnails.of(srcFile).size(300, 300).toFile(thumbFile); // 方案二纯 JDK无依赖适合毕设不引额外包的要求 public static void createThumbnail(File src, File dest, int maxSize) throws IOException { BufferedImage original ImageIO.read(src); if (original null) throw new IOException(无法识别的图片格式); int w original.getWidth(), h original.getHeight(); // 按长边等比缩放保持宽高比 double ratio (double) maxSize / Math.max(w, h); int nw (int) (w * ratio), nh (int) (h * ratio); BufferedImage thumb new BufferedImage(nw, nh, BufferedImage.TYPE_INT_RGB); Graphics2D g thumb.createGraphics(); g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g.drawImage(original, 0, 0, nw, nh, null); g.dispose(); ImageIO.write(thumb, jpg, dest); // 统一输出 jpg体积小 }我一般推荐方案二因为毕设答辩时老师可能问你用了什么第三方库纯 JDK 实现能体现你对图像处理的理解。TYPE_INT_RGB而不是TYPE_INT_ARGB因为缩略图不需要透明通道RGB 更省内存。VALUE_INTERPOLATION_BILINEAR是双线性插值速度和质量的平衡点比默认的最近邻好看很多。输出统一转 jpg即使原图是 png缩略图用 jpg 体积能小一半以上。注意ImageIO.read对某些 CMYK 模式的 jpg 会返回 null要做判空否则直接 NPE。缩略图生成时机建议放在上传成功后同步执行存成原文件名_thumb.jpg列表页直接引用缩略图路径。4. 避坑与排查那些让答辩当场卡壳的细节4.1 中文文件名乱码现象、原因、解决现象上传风景照.jpg数据库里file_name显示成风景照.jpg页面上全是问号。原因Tomcat 8 之前默认ISO-8859-1解析请求getParameter拿到的中文是乱码另外 MySQL 连接串没指定characterEncoding写入时又转了一次。解决三处一起改。第一request.setCharacterEncoding(UTF-8)放在所有getParameter之前第二连接串加useUnicodetruecharacterEncodingutf8mb4第三数据库和表都用utf8mb4。如果 Tomcat 版本老还要在server.xml的 Connector 上加URIEncodingUTF-8。三处缺一处都可能复发这是血泪经验。4.2 上传目录在重新部署后消失现象本地跑得好好的重新部署 war 包或者重启 Tomcat 后之前上传的图片全没了数据库记录还在但图片 404。原因图片存在getRealPath(/upload)返回的目录里这个目录在webapps/项目名/下重新部署 war 会整个覆盖删除。解决把上传目录移到 webapp 之外比如D:/album_upload/或用户主目录下配置一个绝对路径。读取时用FileInputStream从绝对路径读或者配一个 Tomcat 的虚拟目录映射。毕设演示时如果只在本地跑不重新部署问题不明显但答辩前老师让你重新部署一下看看就当场翻车。提前改掉一劳永逸。4.3 数据库连接泄漏导致演示中途卡死现象连续点几十次相册页面越来越慢最后直接无响应重启 Tomcat 才好。原因Connection、PreparedStatement、ResultSet没有关闭连接池耗尽。很多同学只关Connection忘了ResultSet和Statement。解决全部用 try-with-resources像 3.2 节那样写编译器自动帮你关。如果用的是自己封装的DBUtil确保getConnection返回的每个连接都在 finally 或 try-with-resources 里释放。演示前用 JMeter 压一下列表接口跑 100 个并发看连接数是否稳定这个习惯能救命。4.4 图片路径拼接错误导致 404现象数据库里file_path存的是upload/xxx.jpgJSP 里写img src${photo.filePath}结果浏览器请求的是http://localhost:8080/upload/xxx.jpg404。原因相对路径是相对于当前页面 URL 的不是相对于项目根目录。列表页 URL 可能是/photo?actionlist浏览器会拼成/upload/xxx.jpg丢了项目名。解决JSP 里用${pageContext.request.contextPath}/upload/xxx.jpg或者存路径时就存带项目名的完整相对路径。更稳的做法是存绝对路径前缀读取时统一拼。这个坑在本地用 IDEA 跑时因为上下文路径配置不同表现还不一样一定要在真实 Tomcat 部署下测一遍。4.5 事务没提交数据消失现象上传成功提示出来了刷新页面照片也在但重启应用后数据没了。原因JDBC 默认自动提交但如果手动conn.setAutoCommit(false)后忘了commit()数据只在当前连接可见连接关闭就回滚。解决要么保持默认自动提交要么在 finally 里确保commit()或rollback()。批量删除、批量移动分类这类多表操作才需要手动事务单条插入用默认即可。别为了显得专业到处开事务反而容易漏提交。5. 从能跑到能答辩二次开发与验证的几个实用技巧把基础功能跑通只是及格线想让毕设拿高分得在可讲的故事上下功夫。我的习惯是先保证核心链路稳定再加一两个有技术含量的扩展点并且每个扩展点都能说清楚为什么做、怎么做、效果如何。第一个扩展方向是图片 EXIF 信息提取。用metadata-extractor库读取拍摄时间、相机型号、GPS 坐标展示在照片详情页。这个功能代码量不大但答辩时能讲出元数据解析隐私脱敏这些点比单纯增删改查有深度。注意 GPS 信息涉及隐私演示时要么脱敏要么明确说明只存本地不对外。第二个方向是基于标签的检索。在 photo 表旁边加一张 tag 表和关联表支持多标签筛选。这能体现你对多对多关系的理解SQL 里JOIN和GROUP BY的写法也是老师爱问的点。实现时注意标签去重和索引否则数据一多查询就慢。第三个方向是访问统计。记录每张照片的浏览次数用 Redis 或直接 MySQL 计数器都行。毕设场景下 MySQL 足够加一个view_count字段每次详情页访问UPDATE photo SET view_count view_count 1 WHERE id ?。这个功能能引出并发更新缓存的话题答辩时有的聊。验证方面别只靠手点。用 Postman 或 curl 把每个接口单独测一遍确认参数边界空文件、超大文件、非法扩展名、不存在的分类 ID、越权访问别人的照片。越权这个点特别重要——很多源码只校验登录不校验归属A 用户能删 B 用户的照片这是安全漏洞答辩时被指出来很尴尬。修法很简单所有涉及photo_id的操作SQL 里都带上AND user_id ?确保只能操作自己的数据。最后说个习惯源码里所有硬编码的路径、密码、端口全部抽到配置文件里。答辩前把配置改成演示环境的值别现场改代码。我见过太多同学因为db.properties里还写着自家路由器的密码演示时连不上数据库当场社死。把环境配置、启动步骤、测试账号写成一个简短的 README 放在项目根目录自己照着走一遍能跑通才算真正交付。希望帮到你。本文还有配套的精品资源点击获取
返回列表