JPG&PNG图片压缩java实现

2023-10-27

最近项目中有一个需求是关于图片处理的,其实关于图片处理java的BufferedImage类基本上已经可以处理绝大多数需求,但是关于png图片的压缩遇到一点小的阻碍,我们知道png图片与JPG图片最大的区别就是可以保存为透明背景的图片,JPEG就不可以,jpeg是一种有损压缩的图片格式。png是可以转换成JPEG的,但是透明像素会变成白色的,而且图像信息会丢失一部分。png图片压缩起来比较困难。下面给出已经在项目中使用的静态方法实现这些需求。

1、图片文件生成BufferedImage对象:

    /**
     * @param file 图片
     * @return boolean true:符合要求
     * @description 图片文件转化为BufferedImage
     */
    public static BufferedImage fileToBufferedImage(File file) throws IOException {
        if (!file.exists()) {
            return null;
        }
        BufferedImage bufferedImage = ImageIO.read(file);
        return bufferedImage;
    }


    /**
     * @param fileByteArray
     * @return BufferedImage
     * @throws IOException
     * @description 图片文件字节数组转化为BufferedImage
     */
    public static BufferedImage fileToBufferedImage(byte[] fileByteArray) throws IOException {
        if (fileByteArray == null || fileByteArray.length == 0) {
            return null;
        }
        ByteArrayInputStream stream = new ByteArrayInputStream(fileByteArray);
        BufferedImage bufferedImage = ImageIO.read(stream);
        return bufferedImage;
    }

2、BufferedImage对象生成图片文件:有两个写法方式:1-简单方式,2-支持高质量压缩的方式

    /**
     * bufferedImage 转化为文件-简单方式
     * @param bufferedImage
     * @param destFile      获取目标路径
     * @param ext           图片的格式:jpg/png
     * @throws IOException
     */
    public static void bufferedImageToFile1(BufferedImage bufferedImage, String destFile, String ext) throws IOException {
        //使用ImageIO的方法进行输出,记得关闭资源
        OutputStream out = new FileOutputStream(destFile);
        ImageIO.write(bufferedImage, ext, out);
        out.close();
    }

    /**
     * bufferedImage 转化为文件-高质量支持压缩的方式
     * @param bufferedImage
     * @param destFile      获取目标路径
     * @param ext           图片的格式:jpg/png
     * @throws IOException
     */
    public static void bufferedImageToFile2(BufferedImage bufferedImage, String destFile, String ext, float quality) throws IOException {
        Iterator<ImageWriter> imageWriterIter = ImageIO.getImageWritersByFormatName(ext);
        if (imageWriterIter.hasNext()) {
            ImageWriter writer = imageWriterIter.next();
            ImageWriteParam param = writer.getDefaultWriteParam();
            param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
            param.setCompressionQuality(quality);    //质量0-1.0
            File file = new File(destFile);
            FileImageOutputStream out = new FileImageOutputStream(file);
            writer.setOutput(out);
            writer.write(null, new IIOImage(bufferedImage, null, null), param);
            out.close();
            writer.dispose();
        }

    }

3、上面的压缩对于jpg和png图片,因为使用高质量的像素,会导致图片精度很高,图片比较大,现在提供对于jpg和png的图片的压缩代码

      对于jpg图片,我们不断降低压缩质量会较大幅度的降低图片大小,因为jpg图片是有损压缩,质量会降低的比较大,参考代码如下:

    /**
     * bufferedImage 目标将图片压缩到500KB以下,最多压缩5次
     * @param bufferedImage
     * @param ext       图片的格式
     * @param quality   默认1.0            
     * @throws IOException
     */
    public static byte[] bufferedImageToFileByte2Less500K(String tempFilePath, BufferedImage bufferedImage, String ext, float quality) throws IOException {
        Iterator<ImageWriter> imageWriterIter = ImageIO.getImageWritersByFormatName(ext);
        if (imageWriterIter.hasNext()) {
            ImageWriter writer = imageWriterIter.next();
            ImageWriteParam param = writer.getDefaultWriteParam();
            param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
            System.out.println(quality);
            param.setCompressionQuality(quality);    //最高质量
            File file = new File(tempFilePath+ "/" + new Random().nextInt(10)  + "_temp_heap_image_" + quality + "."+ext);
            FileImageOutputStream out = new FileImageOutputStream(file);
            writer.setOutput(out);
            writer.write(null, new IIOImage(bufferedImage, null, null), param);
            ByteArrayOutputStream swapStream = getByteArrayOutputStream(file);
            out.close();
            writer.dispose();
            if(quality <=0.5f || swapStream.toByteArray().length/1024 <=500){
                return swapStream.toByteArray();
            }else{
                quality = quality - 0.1f;
                return bufferedImageToFileByte2Less500K(tempFilePath,bufferedImage,ext,quality);
            }
        }
        return null;
    }

         上面的代码对于png图片,压缩的很有限,因为png是高质量图片,因此,我们要重新想办法,后来找到一个支持png压缩的工具包:OpenViewerFX,可以将png图片压缩的比较小,而且不怎么失真,代码如下:

        需要引入依赖:

    <dependency>
      <groupId>org.jpedal</groupId>
      <artifactId>OpenViewerFX</artifactId>
      <version>6.6.14</version>
    </dependency>
   /**
     * bufferedImage 转化为文件
     * @param bufferedImage
     * @param ext 图片的格式:png
     * @throws IOException
     */
    public static byte[] bufferedImageToFileByte(String tempFilePath, BufferedImage bufferedImage, String ext) throws IOException {
        //使用ImageIO的方法进行输出,记得关闭资源
        File file = new File(tempFilePath+ "/" + new Random().nextInt(10) + "_temp_heap_image."+ext);
        OutputStream out = new FileOutputStream(file);
        ImageIO.write(bufferedImage, ext, out);
        out.close();
        ByteArrayOutputStream swapStream = getByteArrayOutputStream(file);
        //对于图片是jpg或者小于500KB,就直接输出
        if(ext.equalsIgnoreCase("jpg") || swapStream.toByteArray().length/1024 <=500){
            return swapStream.toByteArray();
        }
        File compressOutFile=new File(tempFilePath+ "/" + new Random().nextInt(10) + "_temp_heap_image_cp."+ext);
        //如果图片是png,且大于500KB,则进行压缩
        PngCompressor.compress(file,compressOutFile);
        swapStream = getByteArrayOutputStream(compressOutFile);
        return swapStream.toByteArray();
    }

下面再提供一些工具类:

    /**
     * @param file        图片
     * @param imageWidth  宽
     * @param imageHeight 高
     * @return boolean true:符合要求
     * @description 校验图片比例
     */
    public static boolean checkImageScale(File file, int imageWidth, int imageHeight) throws IOException {

        Boolean result = false;
        if (!file.exists()) {
            return false;
        }
        BufferedImage bufferedImage = ImageIO.read(file);
        int width = bufferedImage.getWidth();
        int height = bufferedImage.getHeight();
        if (imageHeight != 0 && height != 0) {
            int scale1 = imageHeight / imageWidth;
            int scale2 = height / width;
            if (scale1 == scale2) {
                result = true;
            }
        }
        return result;
    }


    /**
     * @param path 图片路径
     * @return java.lang.String base64字符串
     * @description 将图片文件转化为字节数组字符串,并对其进行Base64编码处理
     */
    public static String imgToBase64Str(String path) throws IOException {
        byte[] data = null;
        // 读取图片字节数组
        InputStream in = null;
        try {
            in = new FileInputStream(path);
            data = new byte[in.available()];
            in.read(data);
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        //返回Base64编码过的字节数组字符串
        return Base64.encodeBase64String(data);
    }


    /**
     * 按比例对图片进行缩放
     * @param scale 图片根据比例缩放
     * @throws IOException
     */
    public static BufferedImage zoomByScale(BufferedImage bufferedImage, double scale) throws IOException {

        //获取图片的长和宽
        int width = bufferedImage.getWidth();
        int height = bufferedImage.getHeight();

        //获取缩放后的长和宽
        int _width = (int) (scale * width);
        int _height = (int) (scale * height);

        //获取缩放后的Image对象
        Image _img = bufferedImage.getScaledInstance(_width, _height, Image.SCALE_DEFAULT);

        //新建一个和Image对象相同大小的画布:BufferedImage.TYPE_INT_ARGB
        BufferedImage image = new BufferedImage(_width, _height, BufferedImage.TYPE_INT_ARGB);
        //获取画笔
        Graphics2D graphics = image.createGraphics();
        //将Image对象画在画布上,最后一个参数,ImageObserver:接收有关 Image 信息通知的异步更新接口,没用到直接传空
        graphics.drawImage(_img, 0, 0, null);
        //释放资源
        graphics.dispose();

        return image;

    }

    /**
     * png -支持图片背景透明
     * 图片指定长和宽对图片进行缩放
     * @param width  长
     * @param height 宽
     * @throws IOException
     */
    public static BufferedImage zoomBySize(BufferedImage bufferedImage, int width, int height) {
        //与按比例缩放的不同只在于,不需要获取新的长和宽,其余相同.
        Image _img = bufferedImage.getScaledInstance(width, height, Image.SCALE_DEFAULT);
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        Graphics2D graphics = image.createGraphics();
        graphics.drawImage(_img, 0, 0, null);
        graphics.dispose();
        return image;
    }

    /**
     * @param background 背景
     * @param foreground 前景
     * @param x 前景x坐标
     * @param y 前景y坐标
     * @return
     * @description 将前景图片合成到背景图上
     */
    public static BufferedImage compose(BufferedImage background, BufferedImage foreground, int x, int y) {
        try {
            Graphics2D g = background.createGraphics();
            g.drawImage(foreground, x, y, foreground.getWidth(), foreground.getHeight(), null);
            g.dispose();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return background;

    }


    /**
     * 裁剪png图片的非透明区域,使用最大x和y的边框
     * @param bufferedImage
     * @return
     * @throws IOException
     */
    public static BufferedImage getNotTransparentArea(BufferedImage bufferedImage) throws IOException {

        int w = bufferedImage.getWidth();
        int h = bufferedImage.getHeight();
        int x = 0, y = 0, width = 0, height = 0;
        for (int j = 0; j < h; j++) {
            for (int i = 0; i < w; i++) {
                int dip = bufferedImage.getRGB(i, j);
                if ((dip & 0xff000000) != 0) {
                    if (x == 0 || i < x) {
                        x = i;
                    }
                    if (y == 0 || j < y) {
                        y = j;
                    }
                    if (width == 0 || i > width) {
                        width = i;
                    }
                    if (height == 0 || j > height) {
                        height = j;
                    }
                }
            }
        }
        bufferedImage = bufferedImage.getSubimage(x - 1, y - 1, width - x + 1, height - y + 1);
        return bufferedImage;
    }

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JPG&PNG图片压缩java实现 的相关文章

  • 如何在测试套件中定义 JUnit 方法规则?

    我有一个类 它是 JUnit 测试类的 JUnit 套件 我想定义一个规则on the suite 这是可以做到的 但需要做一些工作 您还需要定义自己的 Suite 运行程序和测试运行程序 然后在测试运行程序中重写 runChild 使用以
  • 如何查看Pocketsphinx词典中是否存在该单词?

    我只是想看看字典文件中是否存在字符串 字典文件位于问题底部 我想检查语音识别器是否可以识别单词 例如 识别器将无法识别字符串ahdfojakdlfafiop 因为字典中没有定义 所以 我可以检查某个单词是否在 pocktsphinx 词典中
  • 如何在 Eclipse 中用阿拉伯语读写

    我在 eclipse 中编写了这段代码来获取一些阿拉伯语单词 然后打印它们 public class getString public static void main String args throws Exception PrintS
  • 由于连接超时,无法通过 ImageIO.read(url) 获取图像

    下面的代码似乎总是失败 URL url new URL http userserve ak last fm serve 126 8636005 jpg Image img ImageIO read url System out printl
  • 通过 InjectMocks Spy 注入对象

    我需要对一个类运行一系列单元测试 该类具有 Autowired Logger 实现 实现的基本思想是 Mock Logger logger InjectMocks TestedClass tested 但我想保存日志输出功能 Mockito
  • 使用 Spring 时实例化对象,用于测试与生产

    使用 Spring 时 应该使用 Spring 配置 xml 来实例化生产对象 并在测试时直接实例化对象 这样的理解是否正确 Eg MyMain java package org world hello import org springf
  • PropertySources 中各种源的优先级

    Spring引入了新的注释 PropertySources对于所有标记为的类 Configuration since 4 0 需要不同的 PropertySource作为论证 PropertySources PropertySource c
  • Java 变量的作用域

    我不明白为什么这段代码的输出是10 package uno public class A int x 10 A int x 12 new B public static void main String args int x 11 new
  • spring - 强制 @Autowired 字段的 cglib 代理

    我有混合堆栈 EJB 和 Spring 为了将 Spring 自动装配到 EJB 我使用SpringBeanAutowiringInterceptor 不确定这是否会影响我遇到的问题 在尝试通过以下方式自动装配 bean 时 Scope p
  • 想要开发像 Facebook 这样的网站 - 处理数百万个请求 - 高性能 [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我想用 Java 开发一个像 Fac
  • Install4j:如何在安装结束时执行命令行 java -jar filename.jar

    在 Intall4j 中 在安装结束时 我只想通过执行如下命令行来初始化某些内容 java jar filename jar 我怎样才能归档这个任务install4j Thanks 将 运行可执行文件或批处理文件 操作添加到 安装屏幕 并设
  • 从 html 页面和 javascript 调用 java webservice

    我正在尝试从 javascript 调用 java 实现的 Web 服务 使用 NetBeans IDE 我读过很多关于 jQuery 和 AJAX 的内容 但我似乎无法掌握它 假设我的 Web 服务 WSDL 位于 http localh
  • 让JScrollPane控制多个组件

    对于我的应用程序 我正在设计一个脚本编辑器 目前我有一个JPanel其中包含另一个JPanel保存行号 位于左侧 以及JTextArea用于允许用户输入代码 位于右侧 目前 我已经实施了JScrollPane on the JTextAre
  • Apache Commons CLI:替代已弃用的 OptionBuilder?

    IntelliJ 显示此示例代码中不推荐使用 OptionBuilderhttp commons apache org proper commons cli usage html http commons apache org proper
  • 如何使用 Mockito 和 Junit 模拟 ZonedDateTime

    我需要模拟一个ZonedDateTime ofInstant 方法 我知道SO中有很多建议 但对于我的具体问题 到目前为止我还没有找到任何简单的解决办法 这是我的代码 public ZonedDateTime myMethodToTest
  • OpenJDK 版本控制

    上下文 我想确保我们系统上安装的 Java 不受 CVE 2022 21449 的影响 java version 给出 openjdk version 11 0 7 2020 04 14 LTS OpenJDK Runtime Enviro
  • 如何在android sdk上使用PowerMock

    我想为我的 android 项目编写一些单元测试和仪器测试 然而 我遇到了一个困扰我一段时间的问题 我需要模拟静态方法并伪造返回值来测试项目 经过一些论坛的调查 唯一的方法是使用PowerMock来模拟静态方法 这是我的 gradle 的一
  • 重写Object类的finalize()方法有什么用?

    据我所知 在java中如果我们想手动调用垃圾收集器 我们可以执行System gc 1 我们在重写的finalize 方法中做了哪些操作 2 如果我们想手动调用JVM垃圾收集器 是否需要重写finalize 方法 我们在重写的 Finali
  • java中如何找到class文件的包

    我正在编写一个使用 class 文件的 java 程序 我希望能够读取文件系统上的 class 文件 使用 InputStream 并确定它所在的包 该 class 文件可能不在一个好的包目录结构中 它可能位于某个随机位置 我怎样才能做到这
  • 使用 eclipse IDE 配置 angularjs

    我想开始使用 AngularJs 和 Java Spring 进行开发 我使用 Eclipse 作为 IDE 我想配置我的 Eclipse 以使这些框架无缝工作 我知道我可能要求太多 但相信我 我已经做了很多研究 你们是我最后的选择 任何帮

随机推荐

  • 英文数字汇总,KMGT,毫微纳

    以5MB为例 现在的习惯是读作 五兆 可是 兆的本意是万亿 在这里却成了百万 5MB的标准读法应该是 五百万字节 网络的带宽 100M 常读作 一百兆 若读作 一百百万 会有人反对 可1000 不是也读作 一千千米 吗 还有气象预报的 五百
  • 讯飞星火大模型申请及测试:诚意满满

    大家好 我是可夫小子 关注AIGC 读书和自媒体 解锁更多ChatGPT AI绘画玩法 加 keeepdance 备注 chatgpt 拉你进群 最近国产大模型跟下饺子似 隔几天就发布一个 厂家发布得起劲 大家看多了也麻木了 而且无一例外都
  • 计算图像帧的平均灰度值

    2016 7 15 在处理视频中 需要对视频流中的图像帧进行区分 分离出其中的亮暗帧图像 区分亮暗图像 是依据图像的平均灰度值来实现的 我们知道 对于一幅灰度图像 每个像素点的灰度值可以通过指针来访问 i j 处的灰度值 img gt im
  • 运行项目出现java.lang.ClassNotFoundException: org.springframework.web.util.IntrospectorCleanupListener

    java lang ClassNotFoundException org springframework web util IntrospectorCleanupListener at org apache catalina loader
  • spyder 出现ValueError: PyCapsule_GetPointer called with incorrect name

    我太难了 经过一堆试验 终于出了坑 总的来说 1 卸载pyqt5 命令 pip uninstall pyqt5 2 重新安装低版本的pyqt5 命令 pip install PyQt5 5 10 1 如果出现pip vendor urlli
  • meta-compilation

    RPython GraalVM 转载于 https my oschina net crcc blog 2239743
  • k8s的pv和pvc创建

    NFS使用PV和PVC 1 配置nfs存储 2 定义PV 实现 下图的pv和pvc测试 pv的定义 这里定义5个PV 并且定义挂载的路径以及访问模式 还有PV划分的大小 vim pv yaml apiVersion v1 kind Pers
  • Pytorch实战笔记(1)——BiLSTM 实现情感分析

    本文展示的是使用 Pytorch 构建一个 BiLSTM 来实现情感分析 本文的架构是第一章详细介绍 BiLSTM 第二章粗略介绍 BiLSTM 就是说如果你想快速上手可以跳过第一章 第三章是核心代码部分 目录 1 BiLSTM的详细介绍
  • JDK11.0.7下载及安装详细教程,步骤解释(win10)

    0 背景知识 JRE Java Runtime Environment JDK Java Development Kit JRE顾名思义是java运行时环境 包含了java虚拟机 java基础类库 是使用java语言编写的程序运行所需要的软
  • HW娱乐板块-你们都是天才~

    接下几天会是各类系统升级改造的时机了 大家都很有想法 就你tm叫韩毅啊 今年你是HW最出名的人物 虽然我不知道他在干什么 但是闸机已经被打穿了 从小我就想当一个百万富翁 我每天加班 可我的梦想却离我越来越遥远 我来到了护网为了实现我的梦想
  • 家里的wifi服务器无响应,宽带wifi服务器无响应

    宽带wifi服务器无响应 内容精选 换一换 当您调用API时 如果遇到 APIGW 开头的错误码 请参见API网关错误码进行处理 正常返回值说明200 OKGET和PUT操作正常返回 201 CreatedPOST操作正常返回 202 Ac
  • 如何用IDaaS云身份认证落地“零信任”安全架构

    题记 2020的RSAC Zero trust 零信任 去年有39家公司打 Zero trust 标签 今年数量激增到91家 可以说零信任理念已被国外同行广泛接受 零信任不仅仅是技术 更是理念的转变 会成为未来十年主流的网络安全架构 为什么
  • 记一次在Tweak中引入Swift framework的过程

    最近想在一个插件里打通App和web端 实现通过web控制App 方案是选用socket io作为服务端和web前端 iOS端socket io是一个纯swift实现的库 目前Theos对swift的支持还不是很完善 但是可以完成一般的工作
  • 大学生团体天梯赛(第八届)

    题目地址 天梯赛 include
  • 浅谈算法和数据结构: 十 平衡查找树之B树

    前面讲解了平衡查找树中的2 3树以及其实现红黑树 2 3树种 一个节点最多有2个key 而红黑树则使用染色的方式来标识这两个key 维基百科对B树的定义为 在计算机科学中 B树 B tree 是一种树状数据结构 它能够存储数据 对其进行排序
  • 复习 OpenGL 纹理映射 总结

    今天系统的复习了一下OpenGL纹理映射 主要参考资料是这两个 OpenGL 教程 http www opengl tutorial org beginners tutorials tutorial 5 a textured cube ht
  • git的简单命令操作,即怎么从github上下载代码到本地

    先到git官网下载自己电脑对应版本的git https git scm com 安装完成后在桌面右键 然后就可以在这里输入命令了 跟linux窗口即命令差不多 接下来就是简单的命令介绍 1 git version 查看当前git安装成功没有
  • 嵌入式期末复习(四)简答题

    1 ARM公司全称是什么 商业模式是什么 列举ST公司常用的ARM内核单片机型号 1 全称 Advanced RISC Machines 2 商业模式 为产品做设计 知识产权付费 3 stm32f101c8t6 stm32f103rct6
  • cgic: CGI的C函数库

    下载回源码包以后 就3个文件 cgic c 函数库capture c 一个很简单的CGI例子 仅仅输出两行提示文字cgictest c 一个演示读取form表单数据的CGI例子 首先在vc6里创建一个空的win32静态库cgic 然后添加c
  • JPG&PNG图片压缩java实现

    最近项目中有一个需求是关于图片处理的 其实关于图片处理java的BufferedImage类基本上已经可以处理绝大多数需求 但是关于png图片的压缩遇到一点小的阻碍 我们知道png图片与JPG图片最大的区别就是可以保存为透明背景的图片 JP