1. 初步使用thumbnailator

1.1 下载依赖

<!-- https://mvnrepository.com/artifact/net.coobird/thumbnailator -->
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.8</version>
</dependency>

1.2 github添加水印图片示例

Thumbnails.of(new File("original.jpg"))//原始图片
.size(160, 160)//指定图片的大小
.rotate(90)//旋转
.watermark(Positions.BOTTOM_RIGHT, ImageIO.read(new File("watermark.png")), 0.5f)//水印的位置、水印图片、透明度0.0f~1.0f
.outputQuality(0.8)//输出图片质量
.toFile(new File("image-with-watermark.jpg"));//输出图片存放位置

1.3 示例

package com.csj2018.o2o.util;

import java.io.File;

import javax.imageio.ImageIO;

import net.coobird.thumbnailator.Thumbnails;
import net.coobird.thumbnailator.geometry.Positions; public class ImageUtil {
public static void main(String[] args) throws Exception {
String basePath = Thread.currentThread().getContextClassLoader().getResource("").getPath();
//先获取新的水印
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");
}
}

2.工具类PathUtil

package com.csj2018.o2o.util;

public class PathUtil {
//获取系统文件分隔符
private static String seperator = System.getProperty("file.separator");
/**
* 返回项目图片根目录
* @return
*/
public static String getImgBasePath() {
String os = System.getProperty("os.name");
String basePath = "";
if(os.toLowerCase().startsWith("win")) {
basePath = "D:/projectdev/image/";
}else {
basePath = "/Users/chenshanju/Desktop/caps/image";
}
basePath = basePath.replace("/", seperator);
return basePath;
}
/**
* 返回店铺图片存储的相对路径
* @param shopId
* @return
*/
public static String getShopImagePath(long shopId) {
String imagePath = "upload/item/shop/"+shopId+"/";
return imagePath.replace("/",seperator);
}
}

问题:为什么不把图片路径设置在classpath下,这样就不用指定绝对路径?

解析:一旦将图片路径设置在classpath,如果工程重新部署,新生成的文件图片就会被删除,除非文件一开始就保存在classpath下,但程序运行过程中,用户会上传新的图片。因此需要将图片存储地址设置在根路径以外,以防止它自动被删除掉。也有公司将图片保存在其他服务器,通过URL引入进来。
关于PathUtil,当前将路径硬写在代码里,是不太合理的,后面利用SpringBoot实现配置化,省心省力。
### 3. 工具类
由于用户传递过来的图片名都是随意命名的,因此有很多是重名的。所以我们不需要用它的名字,用我们系统随机生成的不重名的文件名。
getFileExtension:获取用户上传文件的扩展名
getRandomFileName:获取日期时间+5位随机数
generateThumbnail:处理图片,并返回图片的存放地址

问题:为什么generateThumbnail返回的是相对地址,而非绝对地址?

解析:如果迁移到别的系统上,也是照常能够读取图片,而不需要去改变数据表中shop_img的值。因此需要将shop_img的值存成相对路径的形式。这个图片读出来的时候,会动态给它拼上存储的根路径,进而不管运行在那个系统上,都能读取图片,这样就能达到解耦的功能了。
Spring会将前端上传的文件,用MultipartFile接口去处理。
MultipartFile定义了文件操作的方法,都在实现类CommonsMultipartFile实现了。
这个实现类有一个缺点:目前没有找到一个好的方法,将这实现类初始化,只能通过前台的文件上传空间将文件流传送过来的时候才能将它初始化。因此想把java.io.File转换成FileItem是非常困难的。
CommonsMultipartFile.java
```#java
public class CommonsMultipartFile implements MultipartFile, Serializable {
public CommonsMultipartFile(FileItem fileItem) {
this.fileItem = fileItem;
this.size = this.fileItem.getSize();
}
@Override
public void transferTo(File dest) throws IOException, IllegalStateException {
...
this.fileItem.write(dest);
...
}
}
```
在做UT的时候,不能将File转换成CommonsMultipartFile进行测试,而是直接从前台上传文件将它转换成数据流,或者通过mock的方法将它转换成文件流,再从request里面去获取CommonsMultipartFile对象,这对于service层的UT是比较不现实的。但是CommonsMultipartFile提供了一个transferTo将CommonsMultipartFile转换成java.io.File是比较简单的。

ImageUtil.java

package com.csj2018.o2o.util;

import java.io.File;
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 thumbnail
* @param targetAddr
* @return
*/
public static String generateThumbnail(File thumbnail,String targetAddr) {
String realFileName = getRandomFileName();
String extension = getFileExtension(thumbnail);
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(thumbnail).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;
}
/**
* 根据文件流,处理缩略图,并返回新生成图片的相对路径
* @param thumbnail
* @param targetAddr
* @return
*/
public static String generateThumbnail(CommonsMultipartFile thumbnail,String targetAddr) {
String realFileName = getRandomFileName();
String extension = getFileExtension(thumbnail);
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(thumbnail.getInputStream()).size(200,200).watermark(Positions.BOTTOM_RIGHT,ImageIO.read(new File(basePath+"/watermark.jpg")),0.2f)
.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(CommonsMultipartFile cFile) {
//输入的图片,只需或获取最后一个 . 后面的字符即可
String originalFileName = cFile.getOriginalFilename();
return originalFileName.substring(originalFileName.lastIndexOf("."));
}
/**
* 获取文件的扩展名
* @param cFile
* @return
*/
private static String getFileExtension(File cFile) {
//输入的图片,只需或获取最后一个 . 后面的字符即可
String originalFileName = cFile.getName();
return originalFileName.substring(originalFileName.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;
}
}

校园商铺-4店铺注册功能模块-3thumbnailator图片处理和封装Util的更多相关文章

  1. 校园商铺-4店铺注册功能模块-10店铺注册之js实现

    1. 建立js目录和文件 1.1 建立js目录 在webapp下新建文件夹js,再在js目录下新建shop文件夹. 1.2 js文件 js的功能: 1.从后台获取到店铺分类.区域等是信息,将它填充到前 ...

  2. 校园商铺-4店铺注册功能模块-6店铺注册之Controller层的实现

    1. 从request请求获取获取相关的值 HttpservletRequest request代表的是客户端的请求.当客户端通过http协议访问服务器的时候,http请求头中的所有信息,都封装在这个 ...

  3. 校园商铺-4店铺注册功能模块-8店铺注册之Controller层的改造

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

  4. 校园商铺-4店铺注册功能模块-5店铺注册之Service层的实现

    1. 创建接口 ShopService.java package com.csj2018.o2o.service; import java.io.File; import com.csj2018.o2 ...

  5. 校园商铺-4店铺注册功能模块-4Dto之ShopExecution的实现

    1. DTO:添加店铺的返回类型 问题:为什么不直接用实体类Shop呢? 原因:在操作Shop的时候,必然会有一个状态.添加店铺,添加成功,还是添加失败? 如果添加失败,失败是一个什么状态,这些都是要 ...

  6. 校园商铺-4店铺注册功能模块-1Dao层之更新店铺

    dao层增加更新店铺的方法 package com.csj2018.o2o.dao; import com.csj2018.o2o.entity.Shop; public interface Shop ...

  7. springboot项目整合-注册功能模块开发

    工程简介 准备工作:项目所用到的html界面以及sql文件链接如下:链接: https://pan.baidu.com/s/18loHJiKRC6FI6XkoANMSJg?pwd=nkz2 提取码: ...

  8. SSM到Spring Boot-从零开发校园商铺平台

    第1章 开发准备 本章包含课程介绍,同时讲解开发网站所需要准备的事情,并且带领大家从零开始搭建一个Maven Web. 1-1 课程导学 1-2 开发准备 第2章 项目设计和框架搭建 本章主要先带领大 ...

  9. SSM到Spring Boot从零开发校园商铺平台

    项目目的 特别 由于准备春招,所以希望各位看客方便的话,能去github上面帮我Star一下项目 https://github.com/Draymonders/Campus-Shop emmm, 已经 ...

随机推荐

  1. Java中++操作是同步的吗?为什么?

    不是同步的 因为++操作分为三步实现 内存到寄存器 寄存器自增操作 寄存器写回内存 这三步每一步都可以被打断,不是原子操作,所以不是同步操作

  2. D3.js绘制 颜色:RGB、HSL和插值 (V3版本)

    颜色和插值   计算机中的颜色,常用的标准有RGB和HSL.   RGB:色彩模式是通过对红(Red).绿(Green).蓝(Blue)三个颜色通道相互叠加来得到额各式各样的颜色.三个通道的值得范围都 ...

  3. 案例-开门效果CSS3

    <style> .door { width: 288px; height: 153px; border: 2px solid #333; margin: 150px auto; backg ...

  4. 自行制作yum源仓库

    背景 客户服务器为内网机器,centos7系统,且无法与外网连接.需要部署对应的LANMP环境及其它软件 解决思路 1.在阿里云服务器,利用阿里云的yum源仓库,下载对应软件及关联软件. 2.在客户机 ...

  5. Qt 【无法打开 xxxx头文件】

    经过多次磕碰,终于发现了通用的办法. 测试环境Qt5.5.1 mvcs 比如需要用到QtWin 直接去包含然后运行,but fail, 我去查找他的父类  QtWinExtras Qt自带的自动补全, ...

  6. Dao层结合Service层处理异常

    1. 接口存在异常不利于解耦. 2. 将编译时异常转化为运行时异常或其子类,通知上层,上层可以根据自身能力选择处理或向上抛出. 举例: 将UserDao中的SQLException转化为DaoExce ...

  7. struts2-convention-plugin零配置

    零配置的意思并不是说没有配置,而是通过约定大于配置的方式,大量通过约定来调度页面的跳转而使得配置大大减少. <?xml version="1.0" encoding=&quo ...

  8. java script两个列表之间移动数据

    <select name="b1" id="hao" style="width:100px; height:200px;" size= ...

  9. jQuery方法判断checkbox是否选中以及改变checkbox的选中状态

    jquery判断checked的三种方法: .attr('checked):   //看版本1.6+返回:”checked”或”undefined” ;1.5-返回:true或false .prop( ...

  10. Delphi里面弹出对话框的方法

    1.procedure   ShowMessage(const   Msg:   string); 单元:Dialogsor   QDialogs 例子:showmessage( 'hello '); ...