Thumbnailator:
Thumbnailator是一个用来生成图像缩略图的 Java类库,可生成图片缩略图,支持根据一个目录批量生成缩略图,支持图片缩放,区域裁剪,水印,旋转,保持比例等等
首先,我们的店铺会有图片,每个图片都要存在自己店铺的文件中,并且不能重名,所以上传的图片的路径大部分都不在同一文件,并且经常会重名,所以我们需要单独处理文件的路径和给图片新赋值。
因此一个图片路径名应该是 项目存储图片的根路径/存储商铺的照片的路径/各种商铺/xxxxx.png
实现:
工具类的封装
Step1 添加maven依赖
<!-- https://mvnrepository.com/artifact/net.coobird/thumbnailator -->
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.8</version>
</dependency>
Step2 FileUtil.java
思路:
1. 设置文件存储的根目录
2. 项目中使用的图片按照模块和功能,设置存储的相对路径。
3. 图片的绝对路径: 根目录+相对路径
我们首先处理商铺模块,工具类会随着项目的推进而完善。
首先定义每个商铺对应的图片,存储在对应shopId的目录下。
我们的util包用来存工具类,我们根据不同店铺照片生成不同路径和图片起名我们封装起来存入这个包当做工具直接调用
package com.imooc.o2o.util;
import java.io.File;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
*
* @ClassName: FileUtil
*
* @Description: 文件工具类
*
* @author: Mr.Yang
*
* @date: 2018年5月18日 下午11:32:05
*/
public class FileUtil {
private static final Logger logger = LoggerFactory.getLogger(FileUtil.class);
// 路径分隔符. 在 类UNIX系统上为 '/',在 windows 系统上,它为 '\'
private static String seperator = System.getProperty("file.separator");
/**
*
*
* @Title: getImgBasePath
*
* @Description: 根据不同的操作系统,获取图片的存放路径。
*
* 一般情况下都是将图片存放到专门的图片服务器或者主机上的其他目录。
* 而存放的目录路径,都是以配置项的形式存放在数据库配置表中或者配置文件中。
*
* 这里为了简单起见,我们直接将路径硬编码在代码中。
*
* 图片存储的根路径
* @return
*
* @return: String
*/
public static String getImgBasePath() {
String os = System.getProperty("os.name");
logger.debug("os.name: {}", os);
String basePath = "";
if (os.toLowerCase().startsWith("win")) {
basePath = "D:/o2o/image";
} else {
basePath = "/home/artisan/o2o/image";
}
// 根据操作系统的不同,使用当前操作系统的路径分隔符替换掉,我们写的basePath中的路径分隔符,当然了也可以在basePath赋值的时候直接使用seperator
basePath = basePath.replace("/", seperator);
logger.debug("basePath: {}", basePath);
return basePath;
}
/**
*
*
* @Title: getShopImagePath
*
* @Description: 获取特定商铺的图片的路径,根据shopId来区分。理应配置到数据库配置表中或者配置文件中。
*
* 同样的这里为了简化操作,硬编码
*
* 约定每个店铺下的图片分别存放在对应的店铺id下
*
* 图片存储的相对路径.图片最终存储的位置需要加上getImgBasePath方法返回的basePath
*
* 数据库tb_shop中的shop_img字段存储的是该相对路径,即该方法的返回值
*
* @param shopId
* @return
*
* @return: String
*/
public static String getShopImagePath(long shopId) {
String shopImgPath = "/upload/item/shopImage/" + shopId + "/";
shopImgPath = shopImgPath.replace("/", seperator);
logger.debug("shopImgPath: {}", shopImgPath);
return shopImgPath;
}
/**
*
*
* @Title: getWaterMarkFile
*
* @Description: 水印文件的位置。理应配置到数据库配置表中或者配置文件中。
*
* 同样的这里为了简化操作,硬编码
*
* @return
*
* @return: File
*/
public static File getWaterMarkFile() {
String basePath = FileUtil.getImgBasePath();
String waterMarkImg = basePath + "/watermark/watermark.png";
waterMarkImg = waterMarkImg.replace("/", seperator);
logger.debug("waterMarkImg path: {}", waterMarkImg);
return new File(waterMarkImg);
}
}
Step3 ImageUtil.java
思路:
-
围绕添加水印输出目标图片这个功能展开
-
核心方法generateThumbnails ,创建缩略图中3个关键参数 源图片 水印图 和 目标图片
-
围绕 源图片 水印图 和 目标图片,调用FileUtil中的方法组装目标图片的存储路径以及目标图片的名称。
-
为了方便Service层做单元测试,详见commonsMultipartFileToFile方法的说明
package com.artisan.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 net.coobird.thumbnailator.Thumbnails;
import net.coobird.thumbnailator.geometry.Positions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
public class ImageUtil {
private static final Logger logger = LoggerFactory.getLogger(ImageUtil.class);
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
private static final Random random = new Random();
/**
*
*
* @Title: commonsMultipartFileToFile
*
* @Description: 众所周知,文件上传spring使用CommonsMultipartFile来接收上传过来的文件,
* 而generateThumbnails方法中的第一个入参我们已经调整为了File
* (主要是为了方便service层进行单元测试
* ,在service层无法初始化CommonsMultipartFile,只能在前端传入的时候初始化
* ,我们从底往上搭建项目,还不具备页面,因此做了适当的改造)
*
* 需要将CommonsMultipartFile转换为File
*
* @param cfile
*
* @return: File
*/
public File commonsMultipartFileToFile(CommonsMultipartFile cfile) {
File file = null;
try {
// 获取前端传递过来的文件名
file = new File(cfile.getOriginalFilename());
// 将cfile转换为file
cfile.transferTo(file);
} catch (Exception e) {
e.printStackTrace();
logger.error("commonsMultipartFileToFile failed:{}", e.getMessage());
}
return file;
}
/**
*
*
* @Title: generateThumbnails
*
* @Description: 处理缩略图,并返回绝对路径 (门面照以及商品的小图)
*
* 举例:shop的水印图标要存放在tb_shop的 shop_img字段,所以需要返回 水印图标所在的路径
*
*
*
* @param file
* 需要添加水印的文件
* @param destPath
* 添加水印后的文件的存放目录
* @return
*
* @return: String 返回相对路径的好处是,项目一旦迁移,不会影响,只需要变更basePath即可,尽可能少改动。
* 图片存储的绝对路径=basePath+该路径
*/
public static String generateThumbnails(File file, String destPath) {
// 拼接后的新文件的相对路径
String relativeAddr = null;
try {
// 1.为了防止图片的重名,不采用用户上传的文件名,系统内部采用随机命名的方式
String fileName = generateRandomFileName();
// 2.获取用户上传的文件的扩展名,用于拼接新的文件名
String fileExtensionName = getFileExtensionName(file);
// 3.校验目标目录是否存在,不存在创建目录
validateDestPath(destPath);
// 4.拼接新的文件名
relativeAddr = destPath + fileName + fileExtensionName;
logger.info("图片相对路径 {}", relativeAddr);
// 绝对路径的形式创建文件
String basePath = FileUtil.getImgBasePath();
File destFile = new File(basePath + relativeAddr);
logger.info("图片完整路径 {}", destFile.getAbsolutePath());
// 5.给源文件加水印后输出到目标文件
Thumbnails.of(file).size(500, 500).watermark(Positions.BOTTOM_RIGHT, ImageIO.read(FileUtil.getWaterMarkFile()), 0.25f).outputQuality(0.8).toFile(destFile);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("创建水印图片失败:" + e.toString());
}
return relativeAddr;
}
/**
*
*
* @Title: generateRandomFileName
*
* @Description: 系统时间+5位随机数字
*
* @param file
* @return
*
* @return: String
*/
private static String generateRandomFileName() {
String sysdate = sdf.format(new Date());
// 5位随机数 10000到99999之间 ,下面的取值[ 包括左边,不包括右边 ],满足10000到99999
int rannum = (int) (random.nextDouble() * (99999 - 10000 + 1)) + 10000;
String randomFileName = sysdate + rannum;
logger.debug("fileName: {}", randomFileName);
return randomFileName;
}
/**
*
*
* @Title: getFileExtensionName
*
* @Description: 获取文件的扩展名
*
* @param file
*
* @return: String
*/
private static String getFileExtensionName(File file) {
String fileName = file.getName();
String extension = fileName.substring(fileName.lastIndexOf("."));
logger.debug("extension: {}", extension);
return extension;
}
/**
*
*
* @Title: validateDestPath
*
* @Description:
*
* @param targetAddr
* 图片上传的相对路径
*
* @return: void
*/
private static void validateDestPath(String targetAddr) {
// 获取绝对路径
String realFileParentPath = FileUtil.getImgBasePath() + targetAddr;
// 不存在的话,逐级创建目录
File dirPath = new File(realFileParentPath);
if (!dirPath.exists()) {
dirPath.mkdirs();
}
}
/**
*
*
* @Title: main
*
* @Description: 演示thumbnail的基本用法
*
* @param args
*
* @return: void
*/
public static void main(String[] args) {
try {
// 需要加水印的图片
File souceFile = new File("D:/o2o/artisan.jpg");
// 加完水印后输出的目标图片
File destFile = new File("D:/o2o/artisan-with-watermark.jpg");
// 水印图片
File warterMarkFile = FileUtil.getWaterMarkFile();
logger.info("warterMarkFileName: {}", warterMarkFile.getName());
// 加水印
Thumbnails.of(souceFile).size(500, 500).watermark(Positions.BOTTOM_RIGHT, ImageIO.read(warterMarkFile), 0.25f).outputQuality(0.8).toFile(destFile);
logger.info("水印添加成功,带有水印的图片{}", destFile.getAbsolutePath());
generateRandomFileName();
getFileExtensionName(souceFile);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("创建缩略图失败:" + e.toString());
}
}
}