撸完了登录模块,接着撸商品添加,和商品列表模块:
先亮出数据库:
1 DROP TABLE IF EXISTS products; 2 CREATE TABLE products ( 3 id varchar(100) NOT NULL, 4 name varchar(100) DEFAULT NULL, 5 price double DEFAULT NULL, 6 category varchar(100) DEFAULT NULL, 7 pnum int(11) DEFAULT NULL, 8 description varchar(255) DEFAULT NULL, 9 imageurl varchar(255) DEFAULT NULL, 10 PRIMARY KEY (id) 11 )
一,商品展示页涉及到商品的名称,图片,价格,库存等信息,这里上传功能主要涉及到的就是商品图片上传功能.这里需要导入commons-fileupload-1.2.1.jar,commons-io-1.4.jar两个包.
下面是上传页面的jsp代码:
1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> 2 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 3 <html> 4 <head> 5 <script type="text/javascript"> 6 7 function validateRequired(fieldName){ 8 9 var obj = document.getElementById(fieldName).value; 10 if(obj==""){ 11 document.getElementById(fieldName +"_msg").innerHTML = "<span style=‘color: red‘>"+fieldName+"不能为空</span>"; 12 return false; 13 }else{ 14 document.getElementById(fieldName +"_msg").innerHTML = ""; 15 return true; 16 } 17 } 18 19 </script> 20 </head> 21 22 23 <body> 24 <h3>添加商品的页面</h3> 25 <form method="post" enctype="multipart/form-data" action="${pageContext.request.contextPath}/add"><!-- enctype:设置表达的mime编码为文件上传 --> 26 <table> 27 <tr> 28 <td>商品名称</td> 29 <td> 30 <input type="text" name="name" id="name"> 31 </td> 32 <td id="name_msg"></td> 33 </tr> 34 <tr> 35 <td>商品价格</td> 36 <td> 37 <input type="text" name="price" id="price"> 38 </td> 39 <td id="price_msg"></td> 40 </tr> 41 <tr> 42 <td>商品数量</td> 43 <td> 44 <input type="text" name="pnum" id="pnum"> 45 </td> 46 <td id="pnum_msg"></td> 47 </tr> 48 <tr> 49 <td>商品种类</td> 50 <td> 51 <select name="category"> 52 <option value="图书、音像、数字商品">图书、音像、数字商品</option> 53 <option value="手机、数码、京东通信">手机、数码、京东通信</option> 54 <option value="电脑、办公">电脑、办公</option> 55 <option value="服饰内衣、珠宝首饰">服饰内衣、珠宝首饰</option> 56 </select> 57 </td> 58 <td id="category_msg"></td> 59 </tr> 60 61 <tr> 62 <td>商品图片</td> 63 <td> 64 <input type="file" name="imageurl" id="imageurl"> 65 </td> 66 <td id="imageurl_msg"></td> 67 </tr> 68 <tr> 69 <td>商品描述</td> 70 <td> 71 <textarea rows="5" cols="80" name="description"></textarea> 72 </td> 73 <td id="description_msg"></td> 74 </tr> 75 <tr> 76 <td colspan="3"> 77 <input type="submit" value="添加"> 78 </td> 79 </tr> 80 81 </table> 82 </form> 83 84 </body> 85 </html>
表单递交到servlet后,做个三个操作:
1,判断用户提交的表单是不是文件上传操作.
2,如果是文件上传操作:我在utis包中创建了WebUtils工具类,对文件上传行为进行操作最终返回Map,用于BeanUtils把表单数据绑定到数据实体中;
WebUtils对表单提交是数据进行判断普通数据文本直接添加到map中,对文件上传项(商品图片)把文件保存到本地,然后把保存的路径保存到map中.创建保存图片的路径是要注意:因为一般情况下造作系统中保存的文件个数超过一千时就会影响文件读取的性能,所以这里在utils工具包中创建了UploadUtils工具类.其中包含了用hashcode来获得不同的二级目录,保证足够的文件夹数来保存大量的图片的方法,还有生成uuid给图片命名的方法函数.
3,第二步获得map绑定到数据实体中,然后执行增加操作.
首先是servlet类:
1 public void doPost(HttpServletRequest request, HttpServletResponse response) 2 throws ServletException, IOException { 3 //1,判断是不是文件上传 4 if(!ServletFileUpload.isMultipartContent(request)) 5 { 6 throw new MyRuntimeException("不是文件上传操作......"); 7 } 8 //2,执行文件上传 9 Map<String,String>map=WebUtils.doFileUpload(this.getServletContext().getRealPath("/upload"),request); 10 //3将数据封装到商品bean中 11 Product p=new Product(); 12 try { 13 BeanUtils.populate(p, map); 14 } catch (IllegalAccessException e) { 15 16 e.printStackTrace(); 17 } catch (InvocationTargetException e) { 18 19 e.printStackTrace(); 20 } 21 //4调用业务层插入数据 22 ProductService ps=new ProductServiceImpl(); 23 ps.addProduct(p); 24 response.setContentType("text/html;charset=utf-8"); 25 response.getWriter().print("添加成功...<a href=‘"+request.getContextPath()+"/index.jsp‘>返回</a>"); 26 } 27 }
WebUtils工具类:
1 public class WebUtils { 2 3 public static Map<String, String> doFileUpload(String realPath, 4 HttpServletRequest request) { 5 Map<String,String>map=new HashMap<String, String>(); 6 //拿到工厂类 7 DiskFileItemFactory factory=new DiskFileItemFactory(); 8 ServletFileUpload upload=new ServletFileUpload(factory); 9 upload.setHeaderEncoding("UTF-8");//解决中文名乱码问题 10 try{ 11 List<FileItem>list=upload.parseRequest(request); 12 for(FileItem fileItem:list) 13 { 14 if(fileItem.isFormField()) 15 { 16 //普通输入项 17 String name=fileItem.getFieldName(); 18 String value=fileItem.getString("utF-8"); 19 map.put(name, value); 20 } 21 else{ 22 //文件上传项 23 String filename=fileItem.getName();//文件名可能是aa.txt 也可能是c: \ users \ aa.txt 24 //这两种类型的名字,而我们需要的是aaa.txt这种格式的所以要进行过滤 25 int index=filename.lastIndexOf("\\"); 26 if(index!=-1) 27 { 28 filename=filename.substring(index+1); 29 } 30 //走到这里,文件名肯定是aa.txt形式的了 31 //生成唯一文件名 32 String uuidname=UploadUtils.generateRandonFileName(filename); 33 String randomDir=UploadUtils.generateRandomFileDir(uuidname);// / 1/ 5 34 String imageurl="/upload"+randomDir;// 相对工程根目录的 文件夹 地址了 /upload/1/5/ 35 File file=new File(request.getSession().getServletContext().getRealPath(imageurl));//ServletContext.getRealPath(String virtual path),参数是虚拟路径,返回值是real path 36 file.mkdirs();//创建路径 37 InputStream in=fileItem.getInputStream(); 38 OutputStream out=new FileOutputStream(new File(file,uuidname)); 39 int len=0; 40 byte[] buf=new byte[1024]; 41 while((len=in.read(buf))>0) 42 { 43 out.write(buf, 0, len); 44 } 45 in.close(); 46 out.close(); 47 //删除临时文件 48 fileItem.delete(); 49 map.put("imageurl", imageurl+"/"+uuidname); 50 //图片缩小工具 51 PicUtils pic=new PicUtils(new File(file,uuidname).getCanonicalPath()); 52 pic.resize(180,220); 53 } 54 } 55 return map; 56 } 57 catch(Exception e) 58 { 59 e.printStackTrace(); 60 throw new MyRuntimeException(e); 61 } 62 } 63 64 }
在WebUtils中吧图片保存到文件夹中的时候,我顺便使用了一工具类PicUtils把原图片按照指定的尺寸缩小了一下图片另存了一下,小图片可以在首页显示,大图片可以在商品详细页面中显示.
PicUtils代码:
1 /** 2 * 制作图片缩略图 3 * 4 * @author zhanglei 5 * 6 */ 7 public class PicUtils { 8 private String srcFile; 9 private String destFile; 10 private int width; 11 private int height; 12 private Image img; 13 14 /** 15 * 构造函数 16 * 17 * @param fileName 18 * String 19 * @throws IOException 20 */ 21 public PicUtils(String fileName) throws IOException { 22 File _file = new File(fileName); // 读入文件 23 this.srcFile = fileName; 24 // 查找最后一个. 25 int index = this.srcFile.lastIndexOf("."); 26 String ext = this.srcFile.substring(index); 27 this.destFile = this.srcFile.substring(0, index) + "_s" + ext; 28 img = javax.imageio.ImageIO.read(_file); // 构造Image对象 29 width = img.getWidth(null); // 得到源图宽 30 height = img.getHeight(null); // 得到源图长 31 } 32 33 /** 34 * 强制压缩/放大图片到固定的大小 35 * 36 * @param w 37 * int 新宽度 38 * @param h 39 * int 新高度 40 * @throws IOException 41 */ 42 public void resize(int w, int h) throws IOException { 43 BufferedImage _image = new BufferedImage(w, h, 44 BufferedImage.TYPE_INT_RGB); 45 _image.getGraphics().drawImage(img, 0, 0, w, h, null); // 绘制缩小后的图 46 FileOutputStream out = new FileOutputStream(destFile); // 输出到文件流 47 JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out); 48 encoder.encode(_image); // 近JPEG编码 49 out.close(); 50 } 51 52 /** 53 * 按照固定的比例缩放图片 54 * 55 * @param t 56 * double 比例 57 * @throws IOException 58 */ 59 public void resize(double t) throws IOException { 60 int w = (int) (width * t); 61 int h = (int) (height * t); 62 resize(w, h); 63 } 64 65 /** 66 * 以宽度为基准,等比例放缩图片 67 * 68 * @param w 69 * int 新宽度 70 * @throws IOException 71 */ 72 public void resizeByWidth(int w) throws IOException { 73 int h = (int) (height * w / width); 74 resize(w, h); 75 } 76 77 /** 78 * 以高度为基准,等比例缩放图片 79 * 80 * @param h 81 * int 新高度 82 * @throws IOException 83 */ 84 public void resizeByHeight(int h) throws IOException { 85 int w = (int) (width * h / height); 86 resize(w, h); 87 } 88 89 /** 90 * 按照最大高度限制,生成最大的等比例缩略图 91 * 92 * @param w 93 * int 最大宽度 94 * @param h 95 * int 最大高度 96 * @throws IOException 97 */ 98 public void resizeFix(int w, int h) throws IOException { 99 if (width / height > w / h) { 100 resizeByWidth(w); 101 } else { 102 resizeByHeight(h); 103 } 104 } 105 106 /** 107 * 设置目标文件名 setDestFile 108 * 109 * @param fileName 110 * String 文件名字符串 111 */ 112 public void setDestFile(String fileName) throws Exception { 113 if (!fileName.endsWith(".jpg")) { 114 throw new Exception("Dest File Must end with \".jpg\"."); 115 } 116 destFile = fileName; 117 } 118 119 /** 120 * 获取目标文件名 getDestFile 121 */ 122 public String getDestFile() { 123 return destFile; 124 } 125 126 /** 127 * 获取图片原始宽度 getSrcWidth 128 */ 129 public int getSrcWidth() { 130 return width; 131 } 132 133 /** 134 * 获取图片原始高度 getSrcHeight 135 */ 136 public int getSrcHeight() { 137 return height; 138 } 139 }
UploadUtils工具类:
1 public class UploadUtils { 2 3 /* 4 * 获得hashcode生成二级目录 5 */ 6 public static String generateRandomFileDir(String uuidFileName) { 7 int hashCode=uuidFileName.hashCode(); 8 //一级目录 9 int d1=hashCode&0xf; 10 //二级目录 11 int d2=(hashCode>>4)&0xf; 12 return "/"+d1+"/"+d2; 13 } 14 //随机生成uuid文件名 15 public static String generateRandonFileName(String filename) { 16 return UUID.randomUUID().toString()+filename.substring(filename.lastIndexOf(".")); 17 } 18 /* 19 * 将包含就对路径是文件(c: \d\aa.txt) 返回 aa.txt 文件形式 20 */ 21 public static String subFileName(String fileName) 22 { 23 int index=fileName.lastIndexOf("\\"); 24 if(index!=-1) 25 { 26 fileName=fileName.substring(index+1); 27 } 28 return fileName; 29 } 30 }
添加商品的service层------
ProductService接口:
1 public interface ProductService { 2 3 void addProduct(Product p); 4 5 List<Product> getList(); 6 7 Product getProduct(Product p); 8 9 10 }
ProductServiceImpl类:
1 public class ProductServiceImpl implements ProductService { 2 3 ProductDao pDao=factory.DaoFactory.getInstance().createDao(ProductDao.class); 4 @Override 5 public void addProduct(Product p) { 6 pDao.insert(p); 7 } 8 @Override 9 public List<Product> getList() { 10 11 return pDao.getAll(); 12 } 13 @Override 14 public Product getProduct(Product p) { 15 return pDao.getProduct("id",p.getId()); 16 } 17 18 }
有没有发现上面ProductServiceImpl类中是通过工厂的方式来获取dao的,因为这也可以解耦.今天就不写这个工厂了,放到明天写.不用工厂,咱可以直接new一个出来就行了.明天写了工厂在用工厂的方法获dao;
ProductDaoImpl类的接口ProductDao:
1 public interface ProductDao { 2 3 void insert(Product p); 4 5 List<Product> getAll(); 6 7 Product getProduct(String string, String id); 8 9 void update(Connection connection, OrderItem odi)throws SQLException; 10 11 }
操作商品的ProductDaoImpl类:
1 public class ProductDaoImpl implements ProductDao { 2 3 //生成商品id 4 public String generateProducId(){ 5 6 String value = UUID.randomUUID().toString(); 7 8 int pid = Math.abs(value.hashCode()); 9 return "product-"+pid; 10 } 11 @Override//添加一个商品 12 public void insert(Product p) { 13 QueryRunner runner=new QueryRunner(JdbcUtils.getDataSource()); 14 String sql="insert into products values(?,?,?,?,?,?,?)"; 15 Object[] params={generateProducId(),p.getName(),p.getPrice(),p.getCategory(),p.getPnum(),p.getDescription(),p.getImageurl()}; 16 try { 17 runner.update(sql,params); 18 } catch (SQLException e) { 19 e.printStackTrace(); 20 throw new MyRuntimeException(e); 21 } 22 } 23 24 @Override//获得商品列表 25 public List<Product> getAll() { 26 QueryRunner runner=new QueryRunner(JdbcUtils.getDataSource()); 27 try { 28 return runner.query("select * from products",new BeanListHandler<Product>(Product.class)); 29 } catch (SQLException e) { 30 e.printStackTrace(); 31 throw new MyRuntimeException(e); 32 } 33 } 34 @Override//通过条件key,获取单个商品 35 public Product getProduct(String key, String value) { 36 QueryRunner runner=new QueryRunner(JdbcUtils.getDataSource()); 37 try { 38 return runner.query("select * from products where "+key+"=?",new BeanHandler<Product>(Product.class),value); 39 } catch (SQLException e) { 40 e.printStackTrace(); 41 throw new MyRuntimeException(e); 42 } 43 } 44 @Override//这里用于购买商品时,库存减去购买的数量,最后一篇下单和在线购买中用到 45 public void update(Connection conn, OrderItem odi) throws SQLException { 46 //先获得pnum 47 QueryRunner runner = new QueryRunner(); 48 String sql ="select pnum from products where id=?"; 49 int pnum = (Integer) runner.query(conn, sql , new ScalarHandler(),odi.getProduct_id()); 50 if(pnum<odi.getBuynum()) 51 { 52 //库存不足 53 throw new MyRuntimeException("not enough"); 54 55 } 56 sql= "update products set pnum= pnum-? where id=?"; 57 58 runner.update(conn, sql, odi.getBuynum(),odi.getProduct_id()); 59 } 60 61 }
二,显示商品列表:
这个比较简单,先访问servlet程序获取商品列表,然后传给jsp页面,jsp页面用el表达式获取一下.
servlet涉及到的代码:
1 ProductService ps=new ProductServiceImpl(); 2 List<Product> prod=new ArrayList<Product>(); 3 prod=ps.getList(); 4 request.setAttribute("list", prod); 5 request.getRequestDispatcher("/list_products.jsp").forward(request, response);
service层和dao层的代码都在上面.
jsp页面代码:
1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> 2 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 3 4 5 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 6 <html> 7 <head> 8 9 <title>My JSP ‘list_products.jsp‘ starting page</title> 10 11 <meta http-equiv="pragma" content="no-cache"> 12 <meta http-equiv="cache-control" content="no-cache"> 13 <meta http-equiv="expires" content="0"> 14 <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> 15 <meta http-equiv="description" content="This is my page"> 16 <script type="text/javascript"> 17 function addCart(id){ 18 19 20 // 购物车是 一个 map 对象 , Map<Product,Integer> map, 购物车是 保存 在 session 中的 21 // <a href=""></a> 22 window.location.href=‘${pageContext.request.contextPath}/addCart?id=‘+id; 23 } 24 25 </script> 26 </head> 27 28 <body> 29 <c:if test="${empty list }" > 30 没有商品 31 </c:if> 32 <c:if test="${not empty list }" > 33 <h3 align="center">商品列表</h3> 34 <c:forEach items="${list }" var="product"> 35 36 <img src="/myestore${product.imageurl_s }" style="cursor: pointer;" onclick="jumptodetail(‘${product.id}‘);" align="left"> 37 <div> 38 商品名称:${product.name } 39 </div> 40 <br> 41 <div> 42 商品价格:${product.price } 43 </div> 44 <br> 45 <div> 46 商品种类:${product.category } 47 </div> 48 <br> 49 <div> 50 <c:if test="${product.pnum>0 }"> 51 <font color="green">现货充足</font> 52 </c:if> 53 <c:if test="${product.pnum<=0 }"> 54 <font color="red">库存不足</font> 55 </c:if> 56 </div> 57 <img src="/myestore/img/buy.bmp" style="cursor: pointer;" onclick="addCart(‘${product.id}‘);"> 58 <br clear="left"> 59 </c:forEach> 60 </c:if> 61 62 </body> 63 </html>
看一下效果:
"登录&添加商品&商品列表"撸完收工........
estore商城案例(二)------登录&添加商品&商品列表(下),布布扣,bubuko.com