'How to compress jpg and png images in java?

I used ImageIO library and tried to compress Multipart File but if the extension is jpg then it works fine, but when I used png file then it's not working.

I got this error while executing this code

byte[] byteArr = multipartFile.getBytes();
InputStream inputStream = new ByteArrayInputStream(byteArr);
BufferedImage image = ImageIO.read(inputStream);

public String compressImage(MultipartFile multipartFile, BufferedImage image) throws IOException {
    String filePath = System.getProperty("java.io.tmpdir");
    File compressedImageFile = new File(filePath);
    OutputStream os = new FileOutputStream(compressedImageFile.getName());
    Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(FilenameUtils.getExtension(multipartFile.getOriginalFilename()));
    ImageWriter writer = writers.next();

    ImageOutputStream ios = ImageIO.createImageOutputStream(os);
    writer.setOutput(ios);

    ImageWriteParam param = writer.getDefaultWriteParam();
   
    param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
    param.setCompressionQuality(0.5f);
    
    writer.write(null, new IIOImage(image, null, null), param);
    os.close();
    ios.close();
    writer.dispose();
    return String.valueOf(compressedImageFile);
}

Method threw 'java.lang.UnsupportedOperationException' exception." stack trace

    ERROR TextMessaging:309 - An exception occured while executing sendMessage.json: 
java.lang.UnsupportedOperationException: Compression not supported.
    at javax.imageio.ImageWriteParam.setCompressionMode(ImageWriteParam.java:914) ~[?:1.8.0_301]
    at com.starbucks.amazon.services.impl.ImageCompression.compressImage(ImageCompression.java:36) ~[classes/:?]
    at com.starbucks.amazon.services.impl.ImageCompression$$FastClassBySpringCGLIB$$9a83d3e.invoke(<generated>) ~[classes/:?]
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.6.jar:5.3.6]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:779) ~[spring-aop-5.3.6.jar:5.3.6]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.6.jar:5.3.6]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750) ~[spring-aop-5.3.6.jar:5.3.6]
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123) ~[spring-tx-5.3.6.jar:5.3.6]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388) ~[spring-tx-5.3.6.jar:5.3.6]
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-5.3.6.jar:5.3.6]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.6.jar:5.3.6]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750) ~[spring-aop-5.3.6.jar:5.3.6]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:692) ~[spring-aop-5.3.6.jar:5.3.6]
    at com.starbucks.amazon.services.impl.ImageCompression$$EnhancerBySpringCGLIB$$87686d38.compressImage(<generated>) ~[classes/:?]
    at com.starbucks.amazon.restcalls.TextMessaging.sendMessage(TextMessaging.java:222) ~[classes/:?]
    


Solution 1:[1]

As seen from the stack trace, the problem is param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT). This is perhaps counter-intuitive based on the fact that PNG does indeed support compression, so I fully understand the confusion. However, the API is what it is...

Change:

param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(0.5f);

To:

if (param.canWriteCompressed()) {
    param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
    param.setCompressionQuality(0.5f);
}

PNG format will be written compressed regardless, as PNG always use zlib/deflate compression.

PS: In more recent versions of the JDK, the PNGImageWriteParam supports specifying the compression level (from JDK 9, backport exists for JDK 7 and 8). The above code will work anyway, but it will affect the compression ratio (lower numbers means less quality/more compression). You might want to handle compressions for different formats more explicitly.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1