java服务器内压缩处理图片

通过搜索,选定3种方案参考: thumbnailator,OpenCV,ImageIO(java原生)

OpenCV

先选用了OpenCV,引入了maven包后启动报错

java.lang.UnsatisfiedLinkError: org.opencv.imgcodecs.Imgcodecs.imread_1

晚上搜索了下该错误,根据搜索结果添加以下代码段

System.load(Core.NATIVE_LIBRARY_NAME);

再次启动又出现以下报错

java.lang.UnsatisfiedLinkError: Expecting an absolute path of the library

根据搜索发现这个方案比较麻烦,坑比较多,换用javacv能解决依赖的报错问题,但是依赖项太多,感觉比较重,放弃了这个方案

thumbnailator

看maven仓库发现近一年没有更新版本,担心后续漏洞修复或其他支持问题,放弃这个方案

ImageIO

没有直接选用这种方案是因为看到网上有评论说有可能导致内存溢出(作者未复现),本身压缩需求就是因为要处理内存不足问题,所以对这个方案有疑惑。还有评论说压缩会导致某些图片损坏(不确定是工具问题还是设置问题,作者未复现),所以没有作为第一选择。但是在完成代码编写后此方案确实实现了压缩功能,而且测试阶段未发现上述问题。

第一种方法: 保持纵横比,但是缩减尺寸,生成文件跟原图尺寸不一致,但是细节比较好

public static void main(String[] args) throws IOException {

        File file = new File("C:\\Users\\admin\\Desktop\\5d83e1591902458c8cfb1d2ed181c3f0.jpg");
        byte[] bytes = compressToJpgBySize(file, 1l * 1024 * 1024);

        File outFile = new File("C:\\Users\\admin\\Desktop\\out.jpg");
        OutputStream output = new FileOutputStream(outFile);
        BufferedOutputStream bufferedOutput = new BufferedOutputStream(output);
        bufferedOutput.write(bytes);
        bufferedOutput.close();
        output.close();
}



private static byte[] compressToJpgBySize(File origin, Long picCompressThreshold) throws IOException {
        InputStream input = new FileInputStream(origin);
        byte[] bytes = input.readAllBytes();
        byte[] fileBytes = bytes;

        if(bytes.length > picCompressThreshold){
            BufferedImage originalImage = ImageIO.read(origin);
            int type = originalImage.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : originalImage.getType();
            double originalWidth = originalImage.getWidth();
            double originalHeight = originalImage.getHeight();
            BigDecimal aspectRatio = BigDecimal.valueOf(originalWidth).divide(BigDecimal.valueOf(originalHeight), 4, RoundingMode.HALF_UP);
            // 尽量的去靠近阈值大小,比例缩小后文件大小不固定,猜测是色彩原因,所以多压缩一点比例
            BigDecimal scale = BigDecimal.valueOf(picCompressThreshold).multiply(BigDecimal.valueOf(0.75)).divide(BigDecimal.valueOf(bytes.length),2,RoundingMode.DOWN).sqrt(MathContext.DECIMAL128).setScale(2,RoundingMode.DOWN);
            double newWidth = BigDecimal.valueOf(originalWidth).multiply(scale).setScale(2,RoundingMode.DOWN).doubleValue();
            double newHeight = BigDecimal.valueOf(newWidth).divide(aspectRatio,4,RoundingMode.HALF_UP).doubleValue();

            // 创建一个新的BufferedImage,类型为原始图片的类型
            BufferedImage resizedImage = new BufferedImage((int) newWidth, (int) newHeight, type);

            // 使用Graphics2D来绘制缩小后的图片
            Graphics2D g2d = resizedImage.createGraphics();
            g2d.drawImage(originalImage, 0, 0, (int) newWidth, (int) newHeight, null);
            g2d.dispose();

            // 将缩小后的图片写入到ByteArrayOutputStream中
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ImageIO.write(resizedImage, "jpg", baos);
            fileBytes = baos.toByteArray();
            baos.close();
        }
        input.close();
        return fileBytes;
}

第二种: 保持原图尺寸,生成文件跟原图尺寸一致,但是细节放大看很差

压缩质量设置为0.1时,4.45MB图片压缩至1.04MB,这个压缩后图片大小可控性更差

public static void main(String[] args) throws IOException {

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");

        File file = new File("C:\\Users\\admin\\Desktop\\5d83e1591902458c8cfb1d2ed181c3f0.jpg");
        System.out.println("图片处理开始"+sdf.format(new Date()));
        byte[] bytes = compressToJpgBySize2(file,1l * 1024 * 1024);
        // byte[] bytes = compressToJpgBySize(file, 1l * 1024 * 1024);
        System.out.println("图片处理结束"+sdf.format(new Date()));
        File outFile = new File("C:\\Users\\admin\\Desktop\\out.jpg");
        OutputStream output = new FileOutputStream(outFile);
        BufferedOutputStream bufferedOutput = new BufferedOutputStream(output);
        bufferedOutput.write(bytes);
        bufferedOutput.close();
        output.close();
    }

    private static byte[] compressToJpgBySize2(File origin, Long picCompressThreshold) throws IOException {
        InputStream input = new FileInputStream(origin);
        byte[] bytes = input.readAllBytes();
        byte[] fileBytes = bytes;

        if(bytes.length > picCompressThreshold){
            BufferedImage originalImage = ImageIO.read(origin);
            String formatName = origin.getName().substring(origin.getName().lastIndexOf(".") + 1);
            ImageWriter writer = ImageIO.getImageWritersByFormatName(formatName).next();
            ImageWriteParam param = writer.getDefaultWriteParam();
            param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
            param.setCompressionQuality(0.1f); // 设置压缩质量,范围为0-1,数值越小压缩率越高
            File file = new File("C:\\Users\\admin\\Desktop\\temp.jpg");
            ImageOutputStream ios = ImageIO.createImageOutputStream(file);
            writer.setOutput(ios);
            writer.write(null, new IIOImage(originalImage, null, null), param);
            InputStream res = new FileInputStream(file);
            fileBytes = res.readAllBytes();
            res.close();
            file.deleteOnExit();
        }
        input.close();
        return fileBytes;
    }

后续补充

使用imageIo缩减尺寸压缩一张4.45MB图片到1MB以内在服务器上执行了近3S,效率非常差,图片的压缩是 计算密集型操作(查看本地cpu在图片压缩时飙升到100%),业务需求对实时性没有要求,而且需压缩的图片也并不多,于是将压缩图片任务由定时任务异步操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值