热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开发笔记:校园商铺系统中店铺注册功能模块的Controller层优化与重构

不合理的地方:1. 并不需要将InputStream转换成File类型,直接将InputStream传进入交给CommonsMultipartfile去处理就可以了如果做这样的转换,

不合理的地方:

1. 并不需要将InputStream转换成File类型,直接将InputStream传进入交给CommonsMultipartfile去处理就可以了



如果做这样的转换,每次都需要生成一个空白的文件,然后还需要向文件中写入请求传送过来的文件流,这样不仅仅产生很多垃圾文件,同时有可能造成写入失败,抛出异常的风险,大大地加大了系统的不稳定性。

2. 问题:一开始设计ShopService接口addShop方法的时候,第二个参数不早早设定为InputStream?



原因:体现实际的开发过程。实际的开发中,在设计Service层的时候,只是为了方便对Service层做UT的测试,并且尽快的实现功能,这时第一感觉想到了File。File能够直接读取路径来产生文件流,传入给实现类去做处理,并没有想到Controller层调用时有多么的不便利。

当编写Controller时就感觉MultipartHttpServletRequest->CommonsMultipartFile->File转换不合理的了,因此发现不合理的地方就需要去尽早的改动,因为越早地去做重构,后面维护的成本越低。


  1. 修改接口

package com.csj2018.o2o.service;
import java.io.InputStream;
import com.csj2018.o2o.entity.Shop;
import com.csj2018.o2o.dto.ShopExecution;
public interface ShopService {
ShopExecution addShop(Shop shop,InputStream shopImgInputStream,String fileName);
}

2.修改实现类

package com.csj2018.o2o.service.impl;
import java.io.InputStream;
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.csj2018.o2o.dao.ShopDao;
import com.csj2018.o2o.dto.ShopExecution;
import com.csj2018.o2o.entity.Shop;
import com.csj2018.o2o.enums.ShopStateEnum;
import com.csj2018.o2o.exceptions.ShopOperationException;
import com.csj2018.o2o.service.ShopService;
import com.csj2018.o2o.util.ImageUtil;
import com.csj2018.o2o.util.PathUtil;
@Service
public class ShopServiceImpl implements ShopService {
private Logger logger = LoggerFactory.getLogger(ShopServiceImpl.class);
@Autowired
private ShopDao shopDao;
@Override
@Transactional
public ShopExecution addShop(Shop shop,InputStream shopImgInputStream,String fileName) {
//控制判断,shop是不是包含必需的值
if(shop == null) {
logger.warn("shop== null");
return new ShopExecution(ShopStateEnum.NUll_SHOP);
}
//增加对Shop其他引入类非空的判断
try {
//给店铺信息赋初始值
shop.setEnableStatus(0);
shop.setCreateTime(new Date());
shop.setLastEditTime(new Date());
//添加店铺信息
int effectedNum = shopDao.insertShop(shop);
logger.warn("添加结果:"+effectedNum+"shopId:"+shop.getShopId());
if(effectedNum <= 0) {
throw new ShopOperationException("店铺创建失败");
}else {
if(shopImgInputStream != null) {
//存储图片
try {
addShopImage(shop,shopImgInputStream,fileName);
}catch (Exception e) {
throw new ShopOperationException("addShopImg error:"+e.getMessage());
}
//更新店铺的图片信息
effectedNum = shopDao.updateShop(shop);
if(effectedNum <= 0) {
throw new ShopOperationException("更新图片地址失败");
}
}
}
}catch(Exception e) {
throw new ShopOperationException("addShop error:"+e.getMessage());
}
return new ShopExecution(ShopStateEnum.CHECK,shop);
}
private void addShopImage(Shop shop, InputStream shopImg,String fileName) {
// 获取shop图片目录的相对路径
String dest = PathUtil.getShopImagePath(shop.getShopId());
String shopImgAddr = ImageUtil.generateThumbnail(shopImg, fileName,dest);
shop.setShopImg(shopImgAddr);
}
}

3. 修改工具类

package com.csj2018.o2o.util;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import javax.imageio.ImageIO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import net.coobird.thumbnailator.Thumbnails;
import net.coobird.thumbnailator.geometry.Positions;
public class ImageUtil {
private static String basePath = Thread.currentThread().getContextClassLoader().getResource("").getPath();
private static final SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
private static final Random r = new Random();
private static Logger logger = LoggerFactory.getLogger(ImageUtil.class);
/**
* 将文件流CommonsMultipartFile转换成File
* @param cFile
* @return
*/
public static File transferCommonsMultipartFileToFile(CommonsMultipartFile cFile) {
File newFile = new File(cFile.getOriginalFilename());
try {
cFile.transferTo(newFile);
}catch(IllegalStateException e) {
logger.error(e.toString());
e.printStackTrace();;
}catch (IOException e) {
logger.error(e.toString());
e.printStackTrace();
}
return newFile;
}
/**
* 根据输入流,处理缩略图,并返回新生成图片的相对路径
* @param thumbnailInputStream
* @param targetAddr
* @return
*/
public static String generateThumbnail(InputStream thumbnailInputStream,String fileName ,String targetAddr) {
String realFileName = getRandomFileName();
String extension = getFileExtension(fileName);
makeDirPath(targetAddr);
String relativeAddr = targetAddr + realFileName + extension;
logger.debug("current relativeAddr is:" + relativeAddr);
File dest = new File(PathUtil.getImgBasePath()+relativeAddr);
logger.debug("current complete addr is:" + PathUtil.getImgBasePath()+relativeAddr);
try {
Thumbnails.of(thumbnailInputStream).size(200,200).watermark(Positions.BOTTOM_RIGHT,ImageIO.read(new File(basePath+"/newwater.png")),0.8f)
.outputQuality(0.8f).toFile(dest);
}catch (IOException e) {
logger.error(e.toString());
e.printStackTrace();
}
return relativeAddr;
}
/**
* 创建目标路径所设计的目录,即/a/b/c/xxx.jpg
* 那么a b c 这三个文件夹都得自动创建
* @param targetAddr
*/
private static void makeDirPath(String targetAddr) {
String realFileParentPath = PathUtil.getImgBasePath()+targetAddr;
File dirPath = new File(realFileParentPath);
if(!dirPath.exists()) {
dirPath.mkdirs();//上级目录不存在,也一并创建,同mkdir -p
}
}
/**
* 获取输入文件流的扩展名
* @param thumbnail
* @return
*/
private static String getFileExtension(String fileName) {
//输入的图片,只需或获取最后一个 . 后面的字符即可

return fileName.substring(fileName.lastIndexOf("."));
}
/**
* 生成随机文件名,当前年月日时分秒+5位随机数
* @param args
* @throws Exception
*/
public static String getRandomFileName() {
//获取随机的5位数:10000-99999
int rannum = r.nextInt(89999)+10000;
String nowTimeStr = sDateFormat.format(new Date());
return nowTimeStr + rannum;
}
public static void main(String[] args) throws Exception {
Thumbnails.of(new File(basePath + "/water.png")).size(30, 30).toFile(new File(basePath + "/newwater.png"));
// Thumbnails.of(new File("/Users/chenshanju/Downloads/cat.jpg")).size(200, 200)
// .watermark(Positions.BOTTOM_RIGHT, ImageIO.read(new File(basePath + "newwater.png")), 1.0f)
// .outputQuality(0.8).toFile("/Users/chenshanju/Downloads/newcat.jpg");
}
}

4. 修改测试用例

package com.csj2018.o2o.service;
import static org.junit.Assert.assertEquals;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Date;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import com.csj2018.o2o.BaseTest;
import com.csj2018.o2o.dto.ShopExecution;
import com.csj2018.o2o.entity.Area;
import com.csj2018.o2o.entity.PersonInfo;
import com.csj2018.o2o.entity.Shop;
import com.csj2018.o2o.entity.ShopCategory;
import com.csj2018.o2o.enums.ShopStateEnum;
import net.coobird.thumbnailator.Thumbnails;
public class ShopServiceTest extends BaseTest{
@Autowired
private ShopService shopService;
@Test
public void testAddShop() throws FileNotFoundException {
Shop shop = new Shop();
PersonInfo owner = new PersonInfo();
Area area = new Area();
ShopCategory shopCategory = new ShopCategory();
owner.setUserId(1L);
area.setAreaId(2);
shopCategory.setShopCategoryId(1L);

shop.setOwner(owner);
shop.setArea(area);
shop.setShopCategory(shopCategory);
shop.setShopName("测试de店铺4");
shop.setShopDesc("店铺描述4");
shop.setShopAddr("测试路4号1");
shop.setPhone("1234567892");
shop.setPriority(4);
shop.setCreateTime(new Date());
shop.setEnableStatus(ShopStateEnum.CHECK.getState());
shop.setAdvice("审核中");
File shopImg = new File("/Users/chenshanju/Downloads/cat.jpg");
InputStream is = new FileInputStream(shopImg);
ShopExecution se = shopService.addShop(shop, is,shopImg.getName());
assertEquals(ShopStateEnum.CHECK.getState(), se.getState());
}
}

技术图片


推荐阅读
  • 使用 Azure Service Principal 和 Microsoft Graph API 获取 AAD 用户列表
    本文介绍了一段通用代码示例,该代码不仅能够操作 Azure Active Directory (AAD),还可以通过 Azure Service Principal 的授权访问和管理 Azure 订阅资源。Azure 的架构可以分为两个层级:AAD 和 Subscription。 ... [详细]
  • Explore a common issue encountered when implementing an OAuth 1.0a API, specifically the inability to encode null objects and how to resolve it. ... [详细]
  • 本文介绍如何在 Android 中通过代码模拟用户的点击和滑动操作,包括参数说明、事件生成及处理逻辑。详细解析了视图(View)对象、坐标偏移量以及不同类型的滑动方式。 ... [详细]
  • 本文详细介绍了Java中org.neo4j.helpers.collection.Iterators.single()方法的功能、使用场景及代码示例,帮助开发者更好地理解和应用该方法。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 本文将介绍如何编写一些有趣的VBScript脚本,这些脚本可以在朋友之间进行无害的恶作剧。通过简单的代码示例,帮助您了解VBScript的基本语法和功能。 ... [详细]
  • Explore how Matterverse is redefining the metaverse experience, creating immersive and meaningful virtual environments that foster genuine connections and economic opportunities. ... [详细]
  • 本文介绍了如何使用 Spring Boot DevTools 实现应用程序在开发过程中自动重启。这一特性显著提高了开发效率,特别是在集成开发环境(IDE)中工作时,能够提供快速的反馈循环。默认情况下,DevTools 会监控类路径上的文件变化,并根据需要触发应用重启。 ... [详细]
  • 本文详细介绍了如何在Linux系统上安装和配置Smokeping,以实现对网络链路质量的实时监控。通过详细的步骤和必要的依赖包安装,确保用户能够顺利完成部署并优化其网络性能监控。 ... [详细]
  • 1.如何在运行状态查看源代码?查看函数的源代码,我们通常会使用IDE来完成。比如在PyCharm中,你可以Ctrl+鼠标点击进入函数的源代码。那如果没有IDE呢?当我们想使用一个函 ... [详细]
  • 本文详细介绍了 Dockerfile 的编写方法及其在网络配置中的应用,涵盖基础指令、镜像构建与发布流程,并深入探讨了 Docker 的默认网络、容器互联及自定义网络的实现。 ... [详细]
  • 本文详细介绍了Java中org.eclipse.ui.forms.widgets.ExpandableComposite类的addExpansionListener()方法,并提供了多个实际代码示例,帮助开发者更好地理解和使用该方法。这些示例来源于多个知名开源项目,具有很高的参考价值。 ... [详细]
  • 在前两篇文章中,我们探讨了 ControllerDescriptor 和 ActionDescriptor 这两个描述对象,分别对应控制器和操作方法。本文将基于 MVC3 源码进一步分析 ParameterDescriptor,即用于描述 Action 方法参数的对象,并详细介绍其工作原理。 ... [详细]
  • 探讨如何通过编程技术实现100个并发连接,解决线程创建顺序问题,并提供高效的并发测试方案。 ... [详细]
  • 深入解析Spring Cloud Ribbon负载均衡机制
    本文详细介绍了Spring Cloud中的Ribbon组件如何实现服务调用的负载均衡。通过分析其工作原理、源码结构及配置方式,帮助读者理解Ribbon在分布式系统中的重要作用。 ... [详细]
author-avatar
coolbreeze
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有