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. XX Russia Team Open, High School Programming Contest St Petersburg, Barnaul, Tbilisi, Almaty, Kremenchug, November 30, 2019

    ContestLink easy: AFI medium-easy: BDH medium: CGKL ???: EJ A. Attractive Flowers 签到. B. Blocking th ...

  2. HTML 自定义元素教程

    组件是 Web 开发的方向,现在的热点是 JavaScript 组件,但是 HTML 组件未来可能更有希望. 本文就介绍 HTML 组件的基础知识:自定义元素(custom elements). 文章 ...

  3. IDA静态编译之sub

    int __thiscall sub_10009800(const wchar_t *this, int a2, int a3, HKEY hKey){ } 说明:__thiscall  dll内子函 ...

  4. Ubuntu Server 19配置静态IP

    这个/etc/netplan下默认有个文件50-cloud-init.yaml,直接修改它就行了 sudo vim /etc/netplan/50-cloud-init.yaml 网口名字ens33可 ...

  5. js 默认事件取消

    防止事件捕获和冒泡   :子类的事件会会发父类相同类型的事件, w3c 标准 window.event.stopPropagation也是事件对象(Event)的一个方法,作用是阻止目标元素的冒泡事件 ...

  6. delphi 安卓开发常用

    delphi 安卓开发有几个常用的pas: FMX.Helpers.Android, Androidapi.JNI.Net, Androidapi.JNI.GraphicsContentViewTex ...

  7. 2017-3-8 html基础标签

    <head></head>头标签 <title>页面标签</title> <body>文档的内容可在浏览器中显视的</body> ...

  8. thinkphp 数据缓存

    在ThinkPHP中进行缓存操作,一般情况下并不需要直接操作缓存类,因为系统内置对缓存操作进行了封装,直接采用S方法即可,例如: 缓存初始化 // 缓存初始化 S(array('type'=>' ...

  9. MetalLB自建私有Kubernetes的LoadBalancer负载均衡类型服务

    简介 在私有网络上运行 Kubernetes,和御三家相比,对 LoadBalancer 类型的服务的支持应该是众多表面差异中最醒目的一个了.类型为 LoadBalancer 的服务在 Kuberne ...

  10. word2vec中关于霍夫曼树的

    再谈word2vec 标签: word2vec自然语言处理NLP深度学习语言模型 2014-05-28 17:17 16937人阅读 评论(7) 收藏 举报  分类: Felven在职场(86)    ...