[TOC]
# 步骤 1 : 先运行,看到效果,再学习
先将完整的项目(向老师要相关资料),配置运行起来,确认可用之后,再学习做了哪些步骤以达到这样的效果。
# 步骤 2 : 模仿和排错
在确保可运行项目能够正确无误地运行之后,再严格照着教程的步骤,对代码模仿一遍。
模仿过程难免代码有出入,导致无法得到期望的运行结果,此时此刻通过比较**正确答案** ( 可运行项目 ) 和自己的代码,来定位问题所在。
采用这种方式,**学习有效果,排错有效率**,可以较为明显地提升学习速度,跨过学习路上的各个槛。
# 步骤 3 : 页面截图重启tomcat,通过访问地址
`http://127.0.0.1:8080/tmall_j2ee/admin_productImage_list?productId=4`
可以看到产品图片管理的界面
注: 这productId=4是产品的id,根据你的实际运行情况,采取不同的id值
![](https://box.kancloud.cn/4c883ea97ea4b89537c04ba82570c1db_1542x776.png)
# 步骤 4 : ProductImage,ProductImageDao,ProductImageDaoImpl
见产品管理章节
# 步骤 5 : ProductImageServlet类
ProductImageServlet提供了list,add和delete方法。 edit和update方法直接返回null. 因为界面上没有修改功能
```
package com.dodoke.controller;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.dodoke.bean.Product;
import com.dodoke.bean.ProductImage;
import com.dodoke.dao.inter.ProductImageDao;
import com.dodoke.util.ImageUtil;
import com.dodoke.util.Page;
/**
* Servlet implementation class ProductImageServlet
*/
@WebServlet("/ProductImageServlet")
public class ProductImageServlet extends BaseBackServlet {
private static final long serialVersionUID = 1L;
@Override
public String add(HttpServletRequest request, HttpServletResponse response) {
// 上传文件的输入流
InputStream is = null;
// 提交上传文件时的其他参数
Map<String, String> params = new HashMap<>();
// 解析上传
is = parseUpload(request, params);
// 根据上传的参数生成productImage对象
String type = params.get("type");
int pid = Integer.parseInt(params.get("pid"));
Product p = productDao.get(pid);
ProductImage pi = new ProductImage();
pi.setType(type);
pi.setProduct(p);
productImageDao.add(pi);
// 生成文件
String fileName = pi.getId() + ".jpg";
String imageFolder;
String imageFolder_small = null;
String imageFolder_middle = null;
if (ProductImageDao.type_single.equals(pi.getType())) {
imageFolder = request.getSession().getServletContext().getRealPath("img/productSingle");
imageFolder_small = request.getSession().getServletContext().getRealPath("img/productSingle_small");
imageFolder_middle = request.getSession().getServletContext().getRealPath("img/productSingle_middle");
} else {
imageFolder = request.getSession().getServletContext().getRealPath("img/productDetail");
}
File f = new File(imageFolder, fileName);
f.getParentFile().mkdirs();
// 复制文件
try {
if (null != is && 0 != is.available()) {
try (FileOutputStream fos = new FileOutputStream(f)) {
byte b[] = new byte[1024 * 1024];
int length = 0;
while (-1 != (length = is.read(b))) {
fos.write(b, 0, length);
}
fos.flush();
// 通过如下代码,把文件保存为jpg格式
BufferedImage img = ImageUtil.change2jpg(f);
ImageIO.write(img, "jpg", f);
if (ProductImageDao.type_single.equals(pi.getType())) {
File f_small = new File(imageFolder_small, fileName);
File f_middle = new File(imageFolder_middle, fileName);
f_small.getParentFile().mkdirs();
f_middle.getParentFile().mkdirs();
ImageUtil.resizeImage(f, 56, 56, f_small);
ImageUtil.resizeImage(f, 217, 190, f_middle);
}
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (IOException e) {
e.printStackTrace();
}
return "@admin_productImage_list?pid=" + p.getId();
}
@Override
public String delete(HttpServletRequest request, HttpServletResponse response) {
int id = Integer.parseInt(request.getParameter("id"));
ProductImage pi = productImageDao.get(id);
productImageDao.delete(id);
if (ProductImageDao.type_single.equals(pi.getType())) {
String imageFolder_single = request.getSession().getServletContext().getRealPath("img/productSingle");
String imageFolder_small = request.getSession().getServletContext().getRealPath("img/productSingle_small");
String imageFolder_middle = request.getSession().getServletContext()
.getRealPath("img/productSingle_middle");
File f_single = new File(imageFolder_single, pi.getId() + ".jpg");
f_single.delete();
File f_small = new File(imageFolder_small, pi.getId() + ".jpg");
f_small.delete();
File f_middle = new File(imageFolder_middle, pi.getId() + ".jpg");
f_middle.delete();
} else {
String imageFolder_detail = request.getSession().getServletContext().getRealPath("img/productDetail");
File f_detail = new File(imageFolder_detail, pi.getId() + ".jpg");
f_detail.delete();
}
return "@admin_productImage_list?pid=" + pi.getProduct().getId();
}
@Override
public String edit(HttpServletRequest request, HttpServletResponse response) {
// TODO Auto-generated method stub
return null;
}
@Override
public String update(HttpServletRequest request, HttpServletResponse response) {
// TODO Auto-generated method stub
return null;
}
@Override
public String list(HttpServletRequest request, HttpServletResponse response, Page page) {
int pid = Integer.parseInt(request.getParameter("pid"));
Product p = productDao.get(pid);
List<ProductImage> pisSingle = productImageDao.list(p, ProductImageDao.type_single);
List<ProductImage> pisDetail = productImageDao.list(p, ProductImageDao.type_detail);
request.setAttribute("p", p);
request.setAttribute("pisSingle", pisSingle);
request.setAttribute("pisDetail", pisDetail);
return "admin/listProductImage.jsp";
}
}
```
# 步驟 6:listProductImage.jsp
```
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="java.util.*"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@include file="../include/admin/adminHeader.jsp"%>
<%@include file="../include/admin/adminNavigator.jsp"%>
<script>
$(function() {
$(".addFormSingle").submit(function() {
if (checkEmpty("filepathSingle", "图片文件")) {
$("#filepathSingle").value("");
return true;
}
return false;
});
$(".addFormDetail").submit(function() {
if (checkEmpty("filepathDetail", "图片文件"))
return true;
return false;
});
});
</script>
<title>产品图片管理</title>
<div class="workingArea">
<ol class="breadcrumb">
<li><a href="admin_category_list">所有分类</a></li>
<li><a href="admin_product_list?cid=${p.category.id}">${p.category.name}</a></li>
<li class="active">${p.name}</li>
<li class="active">产品图片管理</li>
</ol>
<table class="addPictureTable" align="center">
<tr>
<td class="addPictureTableTD">
<div>
<div class="panel panel-warning addPictureDiv">
<div class="panel-heading">
新增产品<b class="text-primary"> 单个 </b>图片
</div>
<div class="panel-body">
<form method="post" class="addFormSingle" action="admin_productImage_add" enctype="multipart/form-data">
<table class="addTable">
<tr>
<td>请选择本地图片 尺寸400X400 为佳</td>
</tr>
<tr>
<td><input id="filepathSingle" type="file" name="filepath" /></td>
</tr>
<tr class="submitTR">
<td align="center"><input type="hidden" name="type" value="type_single" /> <input type="hidden" name="pid" value="${p.id}" />
<button type="submit" class="btn btn-success">提 交</button></td>
</tr>
</table>
</form>
</div>
</div>
<table class="table table-striped table-bordered table-hover table-condensed">
<thead>
<tr class="success">
<th>ID</th>
<th>产品单个图片缩略图</th>
<th>删除</th>
</tr>
</thead>
<tbody>
<c:forEach items="${pisSingle}" var="pi">
<tr>
<td>${pi.id}</td>
<td><a title="点击查看原图" href="img/productSingle/${pi.id}.jpg"><img height="50px" src="img/productSingle/${pi.id}.jpg"></a></td>
<td><a deleteLink="true" href="admin_productImage_delete?id=${pi.id}"><span class=" glyphicon glyphicon-trash"></span></a></td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</td>
<td class="addPictureTableTD">
<div>
<div class="panel panel-warning addPictureDiv">
<div class="panel-heading">
新增产品<b class="text-primary"> 详情 </b>图片
</div>
<div class="panel-body">
<form method="post" class="addFormDetail" action="admin_productImage_add" enctype="multipart/form-data">
<table class="addTable">
<tr>
<td>请选择本地图片 宽度790 为佳</td>
</tr>
<tr>
<td><input id="filepathDetail" type="file" name="filepath" /></td>
</tr>
<tr class="submitTR">
<td align="center"><input type="hidden" name="type" value="type_detail" /> <input type="hidden" name="pid" value="${p.id}" />
<button type="submit" class="btn btn-success">提 交</button></td>
</tr>
</table>
</form>
</div>
</div>
<table class="table table-striped table-bordered table-hover table-condensed">
<thead>
<tr class="success">
<th>ID</th>
<th>产品详情图片缩略图</th>
<th>删除</th>
</tr>
</thead>
<tbody>
<c:forEach items="${pisDetail}" var="pi">
<tr>
<td>${pi.id}</td>
<td><a title="点击查看原图" href="img/productDetail/${pi.id}.jpg"><img height="50px" src="img/productDetail/${pi.id}.jpg"></a></td>
<td><a deleteLink="true" href="admin_productImage_delete?id=${pi.id}"><span class=" glyphicon glyphicon-trash"></span></a></td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</td>
</tr>
</table>
</div>
<%@include file="../include/admin/adminFooter.jsp"%>
```
# 步骤 7 : 查询功能讲解
通过产品页面的图片管理访问ProductImageServlet的list()方法
1\. 获取参数pid
2\. 根据pid获取Product对象
3\. 根据product对象获取单个图片的集合pisSingle
4\. 根据product对象获取详情图片的集合pisDetail
5\. 把product 对象,pisSingle ,pisDetail放在request上
6\. 服务端跳转到admin/listProductImage.jsp页面
7\. 在listProductImage.jsp,使用c:forEach 遍历pisSingle
8\. 在listProductImage.jsp,使用c:forEach 遍历pisDetail
ProductImageServlet
```
@Override
public String list(HttpServletRequest request, HttpServletResponse response, Page page) {
int pid = Integer.parseInt(request.getParameter("pid"));
Product p = productDao.get(pid);
List<ProductImage> pisSingle = productImageDao.list(p, ProductImageDao.type_single);
List<ProductImage> pisDetail = productImageDao.list(p, ProductImageDao.type_detail);
request.setAttribute("p", p);
request.setAttribute("pisSingle", pisSingle);
request.setAttribute("pisDetail", pisDetail);
return "admin/listProductImage.jsp";
}
```
listProductImage片段1
```
<c:forEach items="${pisSingle}" var="pi">
<tr>
<td>${pi.id}</td>
<td><a title="点击查看原图" href="img/productSingle/${pi.id}.jpg"><img height="50px" src="img/productSingle/${pi.id}.jpg"></a></td>
<td><a deleteLink="true" href="admin_productImage_delete?id=${pi.id}"><span class=" glyphicon glyphicon-trash"></span></a></td>
</tr>
</c:forEach>
```
listProductImage片段2
```
<c:forEach items="${pisDetail}" var="pi">
<tr>
<td>${pi.id}</td>
<td><a title="点击查看原图" href="img/productDetail/${pi.id}.jpg"><img height="50px" src="img/productDetail/${pi.id}.jpg"></a></td>
<td><a deleteLink="true" href="admin_productImage_delete?id=${pi.id}"><span class=" glyphicon glyphicon-trash"></span></a></td>
</tr>
</c:forEach>
```
# 步骤 8 : 增加功能讲解
增加产品图片分单个和详情两种,其区别在于增加的提交的type类型不一样。
这里就对单个的进行讲解,详情图片的处理同理。
首先, 在listProductImage.jsp准备一个form,提交到admin\_productImage\_add
```
<form method="post" class="addFormSingle" action="admin_productImage_add" enctype="multipart/form-data">
```
接着在ProductImageServlet的add()方法中进行处理
1\. parseUpload 获取上传文件的输入流
2\. parseUpload 方法会修改params 参数,并且把浏览器提交的type,pid信息放在其中
3\. 从params 中取出type,pid信息,并根据这个type,pid,借助productImageDAO,向数据库中插入数据。
4\. 根据request.getSession().getServletContext().getRealPath( "img/productSingle"),定位到存放分类图片的目录
除了productSingle,还有productSingle\_middle和productSingle\_small。 因为每上传一张图片,都会有对应的正常,中等和小的三种大小图片,并且放在3个不同的目录下
5\. 文件命名以保存到数据库的分类对象的id+".jpg"的格式命名
6\. 根据步骤1获取的输入流,把浏览器提交的文件,复制到目标文件
7\. 借助ImageUtil.change2jpg()方法把格式真正转化为jpg,而不仅仅是后缀名为.jpg
8\. 再借助ImageUtil.resizeImage把正常大小的图片,改变大小之后,分别复制到productSingle\_middle和productSingle\_small目录下。
9\. 处理完毕之后,客户端条跳转到admin\_productImage\_list?pid=,并带上pid。
详情图片做的是一样的事情,区别在于复制到目录productDetail下,并且不需要改变大小。
listProductImage
```
<form method="post" class="addFormSingle" action="admin_productImage_add" enctype="multipart/form-data">
<table class="addTable">
<tr>
<td>请选择本地图片 尺寸400X400 为佳</td>
</tr>
<tr>
<td><input id="filepathSingle" type="file" name="filepath" /></td>
</tr>
<tr class="submitTR">
<td align="center"><input type="hidden" name="type" value="type_single" /> <input type="hidden" name="pid" value="${p.id}" />
<button type="submit" class="btn btn-success">提 交</button></td>
</tr>
</table>
</form>
```
ProductImageServlet
```
public String add(HttpServletRequest request, HttpServletResponse response) {
// 上传文件的输入流
InputStream is = null;
// 提交上传文件时的其他参数
Map<String, String> params = new HashMap<>();
// 解析上传
is = parseUpload(request, params);
// 根据上传的参数生成productImage对象
String type = params.get("type");
int pid = Integer.parseInt(params.get("pid"));
Product p = productDao.get(pid);
ProductImage pi = new ProductImage();
pi.setType(type);
pi.setProduct(p);
productImageDao.add(pi);
// 生成文件
String fileName = pi.getId() + ".jpg";
String imageFolder;
String imageFolder_small = null;
String imageFolder_middle = null;
if (ProductImageDao.type_single.equals(pi.getType())) {
imageFolder = request.getSession().getServletContext().getRealPath("img/productSingle");
imageFolder_small = request.getSession().getServletContext().getRealPath("img/productSingle_small");
imageFolder_middle = request.getSession().getServletContext().getRealPath("img/productSingle_middle");
} else {
imageFolder = request.getSession().getServletContext().getRealPath("img/productDetail");
}
File f = new File(imageFolder, fileName);
f.getParentFile().mkdirs();
// 复制文件
try {
if (null != is && 0 != is.available()) {
try (FileOutputStream fos = new FileOutputStream(f)) {
byte b[] = new byte[1024 * 1024];
int length = 0;
while (-1 != (length = is.read(b))) {
fos.write(b, 0, length);
}
fos.flush();
// 通过如下代码,把文件保存为jpg格式
BufferedImage img = ImageUtil.change2jpg(f);
ImageIO.write(img, "jpg", f);
if (ProductImageDao.type_single.equals(pi.getType())) {
File f_small = new File(imageFolder_small, fileName);
File f_middle = new File(imageFolder_middle, fileName);
f_small.getParentFile().mkdirs();
f_middle.getParentFile().mkdirs();
ImageUtil.resizeImage(f, 56, 56, f_small);
ImageUtil.resizeImage(f, 217, 190, f_middle);
}
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (IOException e) {
e.printStackTrace();
}
return "@admin_productImage_list?pid=" + p.getId();
}
```
# 步骤 9 : 删除功能讲解
点击删除超链,进入ProductImageServlet的delete方法
1\. 获取id
2\. 根据id获取ProductImage 对象pi
3\. 借助productImageDAO,删除数据
4\. 如果是单个图片,那么删除3张正常,中等,小号图片
5\. 如果是详情图片,那么删除一张图片
6\. 客户端跳转到admin\_productImage\_list地址
```
public String delete(HttpServletRequest request, HttpServletResponse response) {
int id = Integer.parseInt(request.getParameter("id"));
ProductImage pi = productImageDao.get(id);
productImageDao.delete(id);
if (ProductImageDao.type_single.equals(pi.getType())) {
String imageFolder_single = request.getSession().getServletContext().getRealPath("img/productSingle");
String imageFolder_small = request.getSession().getServletContext().getRealPath("img/productSingle_small");
String imageFolder_middle = request.getSession().getServletContext()
.getRealPath("img/productSingle_middle");
File f_single = new File(imageFolder_single, pi.getId() + ".jpg");
f_single.delete();
File f_small = new File(imageFolder_small, pi.getId() + ".jpg");
f_small.delete();
File f_middle = new File(imageFolder_middle, pi.getId() + ".jpg");
f_middle.delete();
} else {
String imageFolder_detail = request.getSession().getServletContext().getRealPath("img/productDetail");
File f_detail = new File(imageFolder_detail, pi.getId() + ".jpg");
f_detail.delete();
}
return "@admin_productImage_list?pid=" + pi.getProduct().getId();
}
```
# 步骤10:编辑和修改
因为只有图片,就不提供编辑和修改功能。 要做修改,先删除,再增加即可
- 项目简介
- 功能一览
- 前台
- 后台
- 开发流程
- 需求分析-展示
- 首页
- 产品页
- 分类页
- 搜索结果页
- 购物车查看页
- 结算页
- 确认支付页
- 支付成功页
- 我的订单页
- 确认收货页
- 评价页
- 页头信息展示
- 需求分析-交互
- 分类页排序
- 立即购买
- 加入购物车
- 调整订单项数量
- 删除订单项
- 生成订单
- 订单页功能
- 确认付款
- 确认收货
- 提交评价信息
- 登录
- 注册
- 退出
- 搜索
- 前台需求列表
- 需求分析后台
- 分类管理
- 属性管理
- 产品管理
- 产品图片管理
- 产品属性设置
- 用户管理
- 订单管理
- 后台需求列表
- 表结构设计
- 数据建模
- 表与表之间的关系
- 实体类设计
- DAO类设计
- 工具类
- CategoryDao设计
- Service业务类设计
- 后台-分类管理
- 可运行的项目
- 静态资源
- FILTER配合SERVLET
- JSP包含关系
- 查询
- 分页
- 增加
- 删除
- 编辑
- 修改
- 后台其他管理
- 属性管理
- 产品管理
- 产品图片管理
- 产品属性值设置
- 用户管理
- 订单管理