Web项目实战 | 购物系统v1.0 | 开发记录(四) | 实现JQuery图片上传 | 使用JQ插件提交AJAX请求实现发布商品并保存到数据库 | 商品分类分页显示

文章目录

  • 以往课设记录
  • 运行环境
  • 1. 页面效果展示
    • 1.1 首页
    • 1.2 发布商品页面
    • 1.3 商品中心页面
    • 1.4 购买窗口
  • 2. 功能实现
    • 2.1 实现分页查询
      • 2.1.1 分页原理——mysql的指定查询
      • 2.1.2 存储分页信息的方式
      • 2.1.3 实现分页信息变更的Servlet类
      • 2.1.4 实现分页查询的Servlet类
      • 2.1.5 实现分页的JSP页面
    • 2.2 购买窗口商品卡片的弹窗 Modal
    • 2.3 JQuery表单提交插件 & 发布商品
      • 2.3.1 安装JQuery插件
      • 2.3.2 ajaxSubmit请求
      • 2.3.3 servlet保存商品信息
  • 3. 总结
  • 下一部分

以往课设记录


  • Web期末课程设计v1.0(一) | 大学生闲置物品交易系统 | 选择页面模板(附资源),使用 JQuery AJAX实现注册、登陆

  • Web期末课程设计v1.0(二) | 闲优购 | 使用Bootstrap5各组件优化页面,使用AJAX请求实现用户修改信息功能

  • Web期末课程设计v1.0(三) | 闲优购系统 | 基于Bootstrap设计商品页面、使用JQuery实现上传文件并显示到图片组件、使用JSTL标签显示数据库的内容到无序列表

由于遇到了瓶颈…与以往每天一次的总结不同,这次的记录是笔者两天的总结。

运行环境


  • windows10
  • IDEA专业版
  • JDK8
  • HTML5、CSS3、JavaScript
  • JQuery3.6.0
  • Bootstrap5

1. 页面效果展示


1.1 首页

不同于之前的首页样式,笔者这里设计了一个简略的首页…
在这里插入图片描述

1.2 发布商品页面

发布商品首先是通过提交表单到servlet,然后servlet处理这个请求,把表单的信息包括图片名称、描述、类型、价格、图片地址存储到数据库,最后才返回。
在这里插入图片描述

1.3 商品中心页面

这些显示的商品都是从数据库里读取而来的,每次访问页面都会读取数据库对应的商品数据,但为了实现分页,笔者用了较为复杂的逻辑来处理。
在这里插入图片描述在这里插入图片描述

1.4 购买窗口

购买窗口只是一个简单的确认提示框。
在这里插入图片描述

2. 功能实现


2.1 实现分页查询

2.1.1 分页原理——mysql的指定查询

一般的Mysql查表语句为:select * from 表名; 效果如下:
在这里插入图片描述
如果要根据主键查询前n个数据,则为 select * from 表名 limit n 效果如下:
在这里插入图片描述
同理,若要查询 m 后的n个数据,则为 select * from 表名 limit m, n 效果如下:
在这里插入图片描述
综上,如果要实现分页查询,只要先确定每一页展示多少个商品,比如笔者这里是展示8个商品。

然后使用一个page对象表示当前的页面信息,比如当前的页数,如果是第1页。

那么在访问JSP页面时,servlet类里查询的语句为: select * from commodity limit 1, 8

以此类推,第n页的查询语句为: select * from commodity limit n, 8

2.1.2 存储分页信息的方式

基于本学期的web学习,笔者的思路是:

  1. 设计一个page对象,里面有当前页数、每页显示条数、总页数、总记录条数这四个属性

  2. 前端页面JSP只需要获取session域的page对象就可以获取分页查询的一些关键要素

这主要用于显示当前页数,然后在servlet类中同样能使用session域的page对象进行查询操作,查询指定的商品数据,最终将查询结果存储到session域。

Page.java

package com.uni.entity;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@NoArgsConstructor
@AllArgsConstructor
@Data
public class Page {private int pageNow;        // 当前页数private int pageCount;      // 每页显示条数private int totalPage;      // 总页数private int totalRows;      // 总记录
}

2.1.3 实现分页信息变更的Servlet类

这个servlet类是在切换页面时调用的,它的作用仅仅是根据前端的选择来更新page对象的值即修改当前页面的值。

考虑到切换页面有两种方式

  1. 直接上一页或者下一页切换页面
  2. 访问具体的页数

所以需进行不同情况的判断

最终请求转发到的 shop_center 位置是做查询工作,首次访问商品中心就需要通过这个地址。

package com.uni.action;import com.uni.entity.Page;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;@WebServlet(name = "PageAction", urlPatterns = "/shop")
public class PageAction extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String newPage = req.getParameter("page");HttpSession session = req.getSession();Page page = (Page) session.getAttribute("pageInfo");int nowPage = page.getPageNow();switch (newPage) {case "last":page.setPageNow(Integer.max(0, nowPage-1));break;case "next":page.setPageNow((nowPage + 1) % (page.getTotalPage() + 1));break;default:int i = Integer.parseInt(newPage) - 1;if(i >= 0 && i <= page.getTotalPage())page.setPageNow(i);break;}session.setAttribute("pageInfo", page);req.getRequestDispatcher("/shop_center").forward(req, resp);}
}

2.1.4 实现分页查询的Servlet类

在分页查询时,所考虑的问题比较多,比如:

  • 首次访问时,session域没有page对象,所以当page为空时,需要进行初始化。
  • 分页查询需考虑当前的商品类型,因为用于既可能只查看一类商品,也可能查看所有的商品(查询所有数据,笔者使用-1来表示),类似于推荐页面。
  • 分页查询有两种情况:
  1. 用户通过商品类别标签而选择商品信息,这时需要对商品的当前页面进行初始化
    在这里插入图片描述
  2. 通过切换页面访问
    在这里插入图片描述

CommodityQueryAction.java

package com.uni.action;import com.uni.entity.Commodity;
import com.uni.entity.CommodityType;
import com.uni.entity.Page;
import com.uni.service.Service;import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;@WebServlet(name = "CommodityQueryAction", urlPatterns = "/shop_center")
public class CommodityQueryAction extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {HttpSession session = req.getSession();Page page = (Page)session.getAttribute("pageInfo");// 若是第一次访问,则初始化page对象if(page == null) {// 初始化page = new Page();page.setPageCount(8);page.setPageNow(0);session.setAttribute("totalCommodity", Service.commodity.queryAll().size());}int tid = -1;// 当前查询的商品类型,可能为空String tid_param = req.getParameter("ctype");// session域名的nowTypeInteger tid_session = (Integer) session.getAttribute("nowType");// 优先获取地址栏的参数if(tid_param == null && tid_session == null) {tid = -1;}else if(tid_param != null) { // 重置页面为第一页page.setPageNow(0);tid = Integer.parseInt(tid_param);}else tid = tid_session;// 修改分页的属性page.setTotalRows(Service.commodity.countByTypeId(tid));page.setTotalPage(page.getTotalRows() / page.getPageCount());// 根据商品类型分页查询商品数据List<Commodity> commodities = Service.commodity.query(page.getPageNow() * page.getPageCount(), page.getPageCount(), tid);// 根据ID查询商品分类的名称和发布人的名称for (Commodity c : commodities) {c.setTypeName(Service.commodity.queryTypeById(c.getTid()));c.setUserName(Service.user.queryNameById(c.getUid()));}// 设置商品类型信息List<CommodityType> ctypes = Service.commodity.queryAllType();for (CommodityType ctype : ctypes) {// 根据商品类型表的ID去统计商品表中同一个类型的个数ctype.setCount(Service.commodity.countByTypeId(ctype.getId()));}/* 保存页面信息 */session.setAttribute("pageInfo", page);/* 保存商城的商品类型到 session 域 */session.setAttribute("commodityTypes", ctypes);session.setAttribute("nowType", tid);/* 保存商品的信息到 session 域 */session.setAttribute("commodityInfo", commodities);session.setAttribute("nowPage", 0);// ... 重定向resp.sendRedirect("shop_center.jsp");}
}

2.1.5 实现分页的JSP页面

其中标签的类大部分是bootstrap5框架自带的样式,比如mb-5是框架的工具类,表示设置margin-bottom属性设置为等级5的一个大小,具体来说笔者并不清楚5是多少个单位,只知道mb-5可以给它一个不错的效果,能让这些商品卡片下方出现间隔。
商品卡片

<c:forEach items="${sessionScope.commodityInfo}" var="cd"><div class="col-3 mb-5"><div class="card-commodity"><img src="${cd.img}" class="card-img-top"><div class="card-body"><h5 class="card-title text-center">¥${cd.price}h5><h6>商品名称: <span style="color: blue">${cd.name}span>h6><h6 class="card-text" href="#">商品类型: ${cd.typeName}h6><a class="card-text" href="#">商品描述: ${cd.info}a><p>商家: <a href="#">${cd.userName}a>p>div>div>div>c:forEach>

分页按钮

<c:forEach begin="${sessionScope.pageInfo.pageNow}" end="${sessionScope.pageInfo.pageNow + 5}" var="x"><c:if test="${x <= sessionScope.pageInfo.totalPage}"><li class="page-item"><a class="page_a page-link d-block" href="/shop?page=${x+1}">${x+1}a>li>c:if>
c:forEach>

2.2 购买窗口商品卡片的弹窗 Modal

弹窗效果是使用 Bootstrap5框架的 Modal组件
在这里插入图片描述

Bootstrap5 Modal官方文档
在这里插入图片描述
实现的HTML代码:

<div class="modal fade" id="staticBackdrop" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true"><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><h5 class="modal-title" id="staticBackdropLabel">购买商品h5><button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close">button>div><div class="modal-body">是否确认购买?div><div class="modal-footer"><button type="button" class="btn btn-danger" data-bs-dismiss="modal">我再想想button><button type="button" class="btn btn-success">确认购买button>div>div>div>
div>

2.3 JQuery表单提交插件 & 发布商品

2.3.1 安装JQuery插件

下载JQuery 上传文件的官方插件

官方地址
在这里插入图片描述
除了下载源文件,还有一种更方便的办法,直接访问这个网址 JQuery - form插件 源代码
将里面的代码复制,然后在web项目的js文件夹里新建一个js脚本,复制进去后,在HTML页面添加即可。

<script type="text/javascript" src="js/jquery-form-min.js">script>

2.3.2 ajaxSubmit请求

发布商品是通过AJAX请求来实现的,插件提供的ajaxSubmit方法可以直接提交整个表单的数据,因为笔者的表单按钮是在表单外面的,没有在表单里,所以可以通过这种方式来提交表单。

$(document).ready(function (){// $("#form-publish-img").on(function (){// 上传图片$("#form-publish-img").bind("input propertychange",function(event){var reads = new FileReader();var f = $(this).get(0).files[0];var rep = /jpeg|png|gif|bmp|jpg/ig;var gstyle = f.type.split("/")[1];if(rep.test(gstyle)){reads.readAsDataURL(f);reads.onload = function(e) {$("#form-publish-img-show").attr("src",this.result)};}else{alert("图片格式不正确,请上传 jpeg|png|gif|bmp 格式的图片")}})$("#form-button").click(function (){$("#form-publish").ajaxSubmit({//?commodityName=' + $("#input-commodity-name").val()type: 'post',enctype: 'multipart/form-data',url: '/publish',success: function (data){alert('提交成功!' + data)}})});
});

2.3.3 servlet保存商品信息

CommodityPublishAction.java

package com.uni.action;import com.uni.entity.Commodity;
import com.uni.entity.User;import com.uni.service.Service;
import com.uni.utils.FileUtil;import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;@WebServlet(name = "CommodityPublishAction", urlPatterns = "/publish")
public class CommodityPublishAction extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.getWriter().write("不允许使用Get方式请求该路径");}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 【获取用户以及用户填写的商品信息】User user = (User) req.getSession().getAttribute("userInfo");// 【新建商品对象】Commodity commodity = new Commodity();commodity.setUid(user.getId());commodity.setName(FileUtil.read(req, "commodity-name"));commodity.setInfo(FileUtil.read(req, "commodity-info"));commodity.setTid(Integer.parseInt(FileUtil.read(req, "commodityType")));commodity.setPrice(Float.parseFloat(FileUtil.read(req, "commodity-price")));
//        System.out.println("读取表单信息后" + commodity);// 【获取商品名称】String path0 = req.getServletContext().getRealPath( "./") + File.separator + user.getName() + File.separator;String path1 = commodity.getName() + ".jpg";if(FileUtil.saveFile(req, path0, path1)==true) {commodity.setImg(user.getName() + File.separator + path1);Service.commodity.insertCommodity(commodity);resp.getWriter().write("操作成功");} else{resp.getWriter().write("操作失败");}}
}

FileUtil.java

此类负责根据请求对象,去获取请求的表单信息的某个input标签,比如存储图片的input标签用于saveFile方法中,通过IO流处理,将图片保存到服务器本地,而read方法则是获取表单属性中某一个name值为name的标签内容,通过这种方式可直接获取用户在表单填写的商品信息,比如商品名称、价格之类的。

package com.uni.utils;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;
import java.io.*;public class FileUtil {public static String read(HttpServletRequest req, String name){StringBuffer result = new StringBuffer();try {Part part = req.getPart(name);InputStream is = part.getInputStream();InputStreamReader ris = new InputStreamReader(is, "UTF-8");BufferedReader bfr = new BufferedReader(ris);String temp = "";if ((temp = bfr.readLine()) != null)result.append(temp);bfr.close();ris.close();is.close();} catch (IOException e) {e.printStackTrace();} catch (ServletException e) {e.printStackTrace();}return result.toString();}public static boolean saveFile(HttpServletRequest req, String path0, String path1){String path = path0 + path1;// 如果目录不存在则创建File uploadDir = new File(path0);try{if (!uploadDir.exists()) uploadDir.mkdir();Part part = req.getPart("form-publish-img");InputStream is = part.getInputStream();FileOutputStream fos = new FileOutputStream(path);byte[] bty = new byte[1024 * 10];int length =0;while((length=is.read(bty))!=-1){fos.write(bty,0,length);}fos.close();is.close();} catch (Exception e){e.printStackTrace();return false;}return true;}
}

3. 总结


在实现图片保存功能时,笔者在各大博客网站查询了资料,有说使用一个servlet插件,貌似是uploadFile,但是笔者在尝试时,遍历表单的信息,结果只有两个input标签,之后改成servlet原生的对象 Part 则可以正常读取。

整个过程还是比较复杂的,通过这两天的学习,初次使用了JQuery的form表单插件,熟悉了分页查询的实现过程。

下一部分

Web期末课程设计v1.0(五) | 大学生闲置物品交易系统 | 模拟网站埋点实现用户浏览记录的统计与显示


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部