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. 【HDUOJ】1257 最少拦截系统

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1257 题意:经典题. 题解:最长上升子序列. 代码: #include <iostream> ...

  2. Es6中let与const的区别:(神奇的块级作用域)

    所谓的块级作用域:形成一个暂时性的死区:{    } 一.共同点: a:都是用来声明变量: b:都能形成一个块级作用域: c:都只能在声明变量的块级作用域里面有效: 二.不同点: 1.let: a:在 ...

  3. 前端 css 补充内容

    给个菜鸟教程的css教程网址(全的狠!!!): https://www.runoob.com/css/css-tutorial.html 下面是几张css基础样式图:

  4. docker学习---搭建Docker LAMP环境

    1.环境 系统版本:CentOS Linux release 7.4.1708 docker版本:docker-ce-18.09 主机IP:192.168.121.121 2.载入MySQL和PHP镜 ...

  5. RHEL5/6/7中常用命令及命令之间的差异

    System basics Task RHEL5 RHEL6 RHEL7 View subscription information /etc/sysconfig/rhn/systemid /etc/ ...

  6. Vuejs input 和 textarea 元素中使用 v-model 实现双向数据绑定

    demo <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <titl ...

  7. FTT & NTT & 分治FFT

    FFT study from: http://www.orchidany.cf/2019/02/19/FFT1/ https://www.cnblogs.com/zwfymqz/p/8244902.h ...

  8. js 数组排序 根据两个字段属性

    var task = [ {name:"aa", taskLevel:"3",createTime:"2019-12-18"}, {name ...

  9. FFT最新卡常研究

    指针优化并没有什么卵用,反而增大了代码的不可读性. 除了本来的循环顺序优化寻址,在预处理单位复数根时,可以连续存储,以增快寻址速度,细节见代码. 代码给出的是FFT,NTT是一样的. #include ...

  10. 通过base64实现图片下载功能(基于vue)

    1. 使用场景 当我们处理图片下载功能的时候,如果本地的图片,那么是可以通过canvas获得图片的base64的,方法如下.但是如果图片的url存在跨域问题的话,下面的方法将行不通,这时候我们可以另辟 ...