【Java】将Base64格式的图片等比/非等比伸缩至目标尺寸代码实现
等比伸缩
需求
前端页面上传的图片是Base64字符串,需要根据目标尺寸进行伸缩,不能改变图片的比例
代码实现
使用图片处理工具:thumbnailator
引入Maven依赖:
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.8</version>
</dependency>
核心代码:
import net.coobird.thumbnailator.Thumbnails;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.Base64;
import java.util.Objects;
public class Test {
public static final String IMAGE_BASE64_PREFIX_REG = "data:image/[^;]+;base64,";
// 你的测试数据
private static final String IMG_BASE64 = "";
private static final String FILE_PATH = "/Users/xdf/Desktop/Temp/test.png";
private static Integer targetHeight = 400;
private static Integer targetWidth = 400;
public static void main(String[] args) throws IOException {
DecimalFormat df = new DecimalFormat("#.##");
df.setRoundingMode(RoundingMode.FLOOR);
String image = IMG_BASE64.replaceAll(IMAGE_BASE64_PREFIX_REG, "");
BufferedImage bufferedImage = convertBase64ToImage(image);
if (Objects.isNull(bufferedImage)) {
System.err.println("covert base64 image error.");
return;
}
int height = bufferedImage.getHeight();
int width = bufferedImage.getWidth();
// 计算缩放比例,目标尺寸/当前尺寸,大于0则需要放大(目标大),小于0则需要缩放(目标小)
double heightScale = Double.parseDouble(df.format((double) targetHeight / height));
double widthScale = Double.parseDouble(df.format((double) targetWidth / width));
System.out.printf("[current height]: %d, [current width]: %d \n[target height]: %d, [target width]: %d\n", height, width, targetHeight, targetWidth);
// 放大时,需要以目标较长的一边作为放大基准;缩小时,需要以当前尺寸较短的一边为基准(当目标尺寸大时,分子越大,差距越大,当目标尺寸小时,分母越小,差距越大,两种情况都会导致商更大)
double scale = Math.max(heightScale, widthScale);
System.out.printf("[heightScale]: %s, [widthScale]: %s, [final scale]: %s \n", heightScale, widthScale, scale);
BufferedImage targetImage = Thumbnails.of(bufferedImage).scale(scale).asBufferedImage();
ImageIO.write(targetImage, "png", new File(FILE_PATH));
}
public static BufferedImage convertBase64ToImage(String base64String) {
try {
// 解码Base64字符串为字节数组
byte[] imageBytes = Base64.getDecoder().decode(base64String);
// 创建ByteArrayInputStream对象
ByteArrayInputStream bis = new ByteArrayInputStream(imageBytes);
// 将ByteArrayInputStream对象转换为BufferedImage对象
BufferedImage bufferedImage = ImageIO.read(bis);
// 关闭ByteArrayInputStream
bis.close();
// 返回BufferedImage对象
return bufferedImage;
} catch (Exception e) {
System.err.println(e);
}
return null;
}
}
这个方案是保证图片完整的情况下,尽量接近目标尺寸,如果要精确等于目标尺寸,还需要用到裁剪或者下面的非等比方法,做微调。
非等比
使用jdk自带的BufferedImage
实现:
public static String resizeBase64Image(final String originalBase64Image, final Integer targetWidth, final Integer targetHeight) throws Exception {
// base64转为BufferedImage
log.info("start to resize picture...");
long start = System.currentTimeMillis();
BufferedImage originalImage = convertBase64ToImage(originalBase64Image);
assert originalImage != null;
// 将图片伸缩至目标长宽(非等比)
BufferedImage targetImage = resizeImage(originalImage, targetWidth, targetHeight);
String targetBase64Image = convertImageToBase64(targetImage);
log.info("resize image finished. [cost]: {} ms", System.currentTimeMillis() - start);
return targetBase64Image;
}
相关方法:
/**
* 通过BufferedImage图片流调整图片大小
* 在创建新的 BufferedImage 实例时,将图像类型设置为 BufferedImage.TYPE_INT_RGB,它不支持透明度。原始图像的透明区域在目标图像中会变成黑色。
* 如果想保留原始图像的透明区域,需要使用支持透明度的图像类型,比如 BufferedImage.TYPE_INT_ARGB。
*/
public static BufferedImage resizeImage(BufferedImage originalImage, int targetWidth, int targetHeight) throws IOException {
if (originalImage.getWidth() == targetWidth && originalImage.getHeight() == targetHeight) {
return originalImage;
}
Image resultingImage = originalImage.getScaledInstance(targetWidth, targetHeight, Image.SCALE_AREA_AVERAGING);
BufferedImage outputImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_ARGB);
outputImage.getGraphics().drawImage(resultingImage, 0, 0, null);
return outputImage;
}
Base64转BuffedImage:
public static BufferedImage convertBase64ToImage(String base64String) {
try {
// 解码Base64字符串为字节数组
byte[] imageBytes = Base64.getDecoder().decode(base64String);
// 创建ByteArrayInputStream对象
ByteArrayInputStream bis = new ByteArrayInputStream(imageBytes);
// 将ByteArrayInputStream对象转换为BufferedImage对象
BufferedImage bufferedImage = ImageIO.read(bis);
// 关闭ByteArrayInputStream
bis.close();
// 返回BufferedImage对象
return bufferedImage;
} catch (Exception e) {
log.info("convertBase64ToImage error: {}", e.getMessage());
}
return null;
}
BuffedImage转Base64:
public static String convertImageToBase64(BufferedImage image) {
try {
// 创建字节数组输出流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 将BufferedImage对象写入字节数组输出流
ImageIO.write(image, "png", baos);
// 关闭字节数组输出流
baos.close();
// 获取字节数组
byte[] imageBytes = baos.toByteArray();
// 返回Base64编码的字符串
return Base64.getEncoder().encodeToString(imageBytes);
} catch (Exception e) {
log.info("convertImageToBase64 error: {}", e.getMessage());
}
return null;
}