校园商铺-4店铺注册功能模块-3thumbnailator图片处理和封装Util
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;
}
}