起因
项目的需求:需要将传入图片高清等比压缩,经过调查,最后选择了Thumbnailator。但它在等比压缩的时候需要获取原图片的宽高,原本是使用ImageIo.read()来获取的,然后传入大图片的时候提示了堆异常,虽然这种错误一般通过修改jvm的启动参数xmx xms就可以解决,但是我想有没有办法能够用更优雅的方式解决这个问题,而不是单单只增加堆内存。
解决过程
想法很简单,既然用ImageIO读取会异常,那我换另一种方式不就行了么,然后偶尔的情况下看到阿里的ossJdk是支持对图片进行一系列操作的,例如获取信息,加水印,缩放等等。嗯嗯发现的有点晚,而后在这些接口文档里面我看了熟悉的imageWidth和imageHeigh,没错这就是我想要的。因为项目是先上传原图在压缩进行ocr识别的,所以可以用这种。 下面附上代码:阿里的代码返回的竟然是ossObject,开始的时候着实让我费解了一会,之后才弄清楚了它是把返回信息房子里面response中的。
public String gainImageInfo(String fileName) throws IOException {
OSSClient ossClient = new OSSClient(businessConfig.getEndpoint(), businessConfig.getAccessKeyId(),
businessConfig.getAccessKeySecret());
String style = "image/info";
GetObjectRequest request = new GetObjectRequest(businessConfig.getBucketName(), fileName);
request.setProcess(style);
InputStream inputStream = ossClient.getObject(request).getObjectContent();
StringBuilder sb = new StringBuilder();
String line;
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
while ((line = br.readLine()) != null) {
sb.append(line);
}
String str = sb.toString();
log.info("数据:{}", str);
// 关闭OSSClient。
inputStream.close();
ossClient.shutdown();
return str;
}
下面展示的是如何处理结果:
String height = "ImageHeight";
String width = "ImageWidth";
JSON.parseObject(jsonObject.getString(height)).getInteger("value")
JSON.parseObject(jsonObject.getString(width)).getInteger("value")
下面的是图片压缩的代码:
/**
* 图片压缩
*
* @param data 字节流
* @param imageHeight 图片高
* @param imageWidth 图片宽
* @param maxSize 最大大小
* @return byte[]
* @throws IOException IO异常
*/
public static byte[] commpressPicForScale(byte[] data, Integer imageHeight, Integer imageWidth, Integer maxSize) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
long srcFileSize = data.length;
double accuracy = getAccuracy(srcFileSize / 1024);
//获取图片信息
int srcWidth = imageWidth;
int srcHeight = imageHeight;
//先转换成jpg
Thumbnails.Builder builder = Thumbnails.of(new ByteArrayInputStream(data)).outputFormat("jpg");
//宽高均小,指定原大小
builder.size(srcWidth, srcHeight);
// 写入到内存
builder.toOutputStream(baos);
// 递归压缩,直到目标文件大小小于desFileSize
byte[] bytes = commpressPicCycle(baos.toByteArray(), maxSize, accuracy);
// 输出到文件
baos.close();
return bytes;
} catch (Exception e) {
log.error("图片压缩错误!"+e.getMessage());
return new byte[]{};
} finally {
baos.close();
}
}
/**
* 迭代压缩
*
* @param bytes 字节流
* @param desFileSize 目标大小
* @param accuracy 根据一定的换算公式计算出的最佳质量比
* @return byte[]
* @throws IOException io异常
*/
private static byte[] commpressPicCycle(byte[] bytes, long desFileSize, double accuracy) throws IOException {
long srcFileSizeJPG = bytes.length;
// 2、判断大小,如果小于500kb,不压缩;如果大于等于500kb,压缩
if (srcFileSizeJPG <= desFileSize * 1024) {
return bytes;
}
// 计算宽高
BufferedImage bim = ImageIO.read(new ByteArrayInputStream(bytes));
int srcWdith = bim.getWidth();
int srcHeigth = bim.getHeight();
int desWidth = new BigDecimal(srcWdith).multiply(
BigDecimal.valueOf(accuracy)).intValue();
int desHeight = new BigDecimal(srcHeigth).multiply(
BigDecimal.valueOf(accuracy)).intValue();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Thumbnails.of(new ByteArrayInputStream(bytes)).size(desWidth, desHeight).outputQuality(accuracy).toOutputStream(baos);
return commpressPicCycle(baos.toByteArray(), desFileSize, accuracy);
}
/**
* 自动调节精度(经验数值)
*
* @param size 源图片大小
* @return 图片压缩质量比
*/
private static double getAccuracy(long size) {
double accuracy;
if (size < 900) {
accuracy = 0.88;
} else if (size < 2047) {
accuracy = 0.6;
} else if (size < 3275) {
accuracy = 0.55;
} else {
accuracy = 0.48;
}
return accuracy;
}
虽然说处理方式不过优雅 但实际上满足了需求也稍微优化了点代码。最后补充个当天学到的合并List中一个类中相同的多个属性值的数据,并累加指定字段数据的写法:
List<JidianActivityCouponDO> couponDOList = new ArrayList<>();
couponDOS.parallelStream().collect(Collectors.groupingBy(
o -> (o.getRelationCouponId() + "" + o.getJidian()))).forEach(
(id, transfer) -> transfer.stream().reduce((a, b) -> {
JidianActivityCouponDO couponDO = new JidianActivityCouponDO();
couponDO.setRelationCouponId(a.getRelationCouponId());
couponDO.setJidian(a.getJidian());
couponDO.setId(a.getId());
couponDO.setCouponName(a.getCouponName());
couponDO.setCouponQuantity(a.getCouponQuantity());
couponDO.setCouponConvertQuantity(a.getCouponConvertQuantity() + b.getCouponConvertQuantity());
couponDO.setCouponUsedQuantity(a.getCouponUsedQuantity() + b.getCouponUsedQuantity());
return couponDO;
}).ifPresent(couponDOList::add)
);
}
愉快收工