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. python全栈开放实践第三版第一章的练习题完成情况

    练习题: 1.简述编译型与解释型语言的区别,且分别列出你知道哪些语言属于编译型,哪些数以解释型.1 编译型:只须编译一次就可以把源代码编译成机器语言,后面的执行无须重新编译,直接使用之前的编译结果就可 ...

  2. 37-Ubuntu-用户管理-02-查看用户信息

    查看用户信息 序号 命令 作用 01 id 用户名 查看用户UID和GID信息 02 cat -n /etc/passwd 查看用户详细信息,参数-n显示行号 03 cat -n /etc/group ...

  3. 3-Windows-CMD启动mysql服务-连接本地mysql服务-连接远程mysql服务

    转自: https://jingyan.baidu.com/article/84b4f565b77a5660f6da32d4.html 备注: 如果在连接远程mysql服务,无法连接时,可能是远程my ...

  4. git stash封存分支 以及关于开发新功能的处理

    有种情况,我们要修复项目的bug时,但别的分支有修改的代码,要修复的bug可能会影响(所有分支共用一个暂存区).可以单独创建一个bug分支,用于修复和提交bug,在修改前可以先stash封存分支修改的 ...

  5. 利用dynamic解决匿名对象不能赋值的问题

    原文:利用dynamic解决匿名对象不能赋值的问题 关于匿名对象 匿名对象是.Net Framework 3.0提供的新类型,例如: }; 就是一个匿名类,搭配Linq,可以很灵活的在代码中组合数据, ...

  6. C# 消息队列 多线程 委托

    发消息 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; ...

  7. day01 mysql认识 安装 配置 起服务 密码 字符集 用户授权

    day01 mysql      一.认识mysql     关系型数据库:         最流行的关系型数据库管理系统,支持大型数据库,处理上千万条记录         关系型: oracle,  ...

  8. 解决 php artisan route:list 报错oauth-private.key文件不存在或不可读的

    进入项目根目录命令行执行 php artisan passport:install 然后执行php artisan route:list,会提示 Class App\Http\Controllers\ ...

  9. Java KMP算法代码

    1. KMP 算法(字符串匹配算法)较 BF(朴素的字符串匹配)算法有哪些改进 1) 在主串和子串匹配的过程中,主串不再回退,只改变子串的比较位置. 2) 为子串生成对应的next数组,每次匹配失败, ...

  10. python数据读取路径为啥要用双反斜杠?

    Window下python读取数据路径可以有三种表示方式: (1)'c:\\a.txt' ——>转义的方式.表示这里\\是一个普通\字符,不容易出错(2)r'c:\a.txt' ——>声明 ...