Base64是一种将二进制数据编码为ASCII字符串的方法。它通过将3个字节的二进制数据转换为4个可打印字符的ASCII字符,从而将二进制数据转换为可传输的文本格式。Base64编码常用于传输图片或音频文件。Base64编码可以保证数据在传输过程中不丢失,同时可以避免某些系统不支持二进制数据的问题。
但是Base64转换成图片之前,如何压缩目标图片的大小呢?本文提供两种方式。
方法一:按尺寸压缩(不保证图片质量)
/**
* 去掉Base64图片数据的前缀
*
* @param base64Str 含前缀的Base64字符串
* @return 不含前缀的Base64字符串
*/
private static String removeBase64Prefix(String base64Str) {
// 使用正则表达式去掉"data:image/\w+;base64,"前缀
Pattern pattern = Pattern.compile("^data:image/\\w+;base64,");
Matcher matcher = pattern.matcher(base64Str);
return matcher.replaceFirst("");
}
/**
* 添加Base64图片数据的前缀
*
* @param base64Str 不含或含其他前缀的Base64字符串
* @param prefix 想要添加的Base64前缀,默认为"data:image/png;base64,"
* @return 含指定前缀的Base64字符串
*/
public static String addBase64Prefix(String base64Str, String prefix) {
// 检查是否已存在指定的前缀,如果有,则直接返回
if (base64Str.startsWith(prefix)) {
return base64Str;
}
return prefix + base64Str;
}
/**
* 压缩base64编码至200K以内
*
* @param base64Img
* @return
*/
public static String resizeImageTo200K(String base64Img) {
try {
byte[] bytes1 = Base64.getDecoder().decode(removeBase64Prefix(base64Img));
System.out.println( bytes1.length);
InputStream stream = new ByteArrayInputStream(bytes1);
BufferedImage src = ImageIO.read(stream);
// 压缩的尺寸
BufferedImage output = output=Thumbnails.of(src).size(640, 480).asBufferedImage();
String base64 = imageToBase64(output);
double minScalingFactor = 0.7; // 设置最小缩放因子
if (base64.length() - base64.length() / 8 * 2 > 200000) {
double scalingFactor = Math.max(minScalingFactor, 1 - (base64.length() / 200000));
output = Thumbnails.of(output).scale(scalingFactor).asBufferedImage();
base64 = imageToBase64(output);
}
return addBase64Prefix(base64, "data:image/png;base64,");
} catch (Exception e) {
return addBase64Prefix(base64Img, "data:image/png;base64,");
}
}
// BufferedImage转换成base64,在这里需要设置图片格式,如下是png格式图片:
public static String imageToBase64(BufferedImage bufferedImage) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
ImageIO.write(bufferedImage, "png", baos);
} catch (IOException e) {
}
String str = new String(Base64.getEncoder().encode(baos.toByteArray()));
return str;
}
}
方法二:等质量压缩
/**
* 压缩base64编码图片至目标大小附近,尽量保持图片质量
*
* @param base64Img base64编码的图片字符串
* @param targetSize 目标大小(例如:200KB)
* @return 调整大小后的base64编码图片字符串
*/
public static String resizeImageToTargetSize(String base64Img, int targetSize) {
try {
String s = removeBase64Prefix(base64Img);
byte[] imageBytes = Base64.getDecoder().decode(s);
ByteArrayInputStream inputStream = new ByteArrayInputStream(imageBytes);
BufferedImage src = ImageIO.read(inputStream);
// 初始化压缩质量为最高,根据需要逐步降低
float quality = 1.0f;
float step = 0.1f;
boolean compressMore = true;
byte[] compressedBytes = null;
while (compressMore) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpeg");
if (!writers.hasNext()) throw new IllegalStateException("No writers found");
ImageWriter writer = writers.next();
ImageWriteParam param = writer.getDefaultWriteParam();
// 设置压缩质量
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(quality);
MemoryCacheImageOutputStream memStream = new MemoryCacheImageOutputStream(outputStream);
writer.setOutput(memStream);
IIOImage outputImage = new IIOImage(src, null, null);
writer.write(null, outputImage, param);
writer.dispose();
compressedBytes = outputStream.toByteArray();
int currentSize = compressedBytes.length;
System.out.println("尝试质量: " + quality + ", 大小: " + currentSize);
// 如果压缩后大小仍超过目标大小,降低质量继续尝试;否则停止循环
if (currentSize > targetSize) {
quality -= step;
if (quality < 0.1f) { // 防止质量降得过低
quality = 0.1f;
}
} else {
compressMore = false;
}
}
String base64Encoded = Base64.getEncoder().encodeToString(compressedBytes);
return addBase64Prefix(base64Encoded, "data:image/png;base64,");
} catch (Exception e) {
return base64Img; // 如果压缩失败,返回原图
}
}