Java 实现图片压缩

1、具体实现
  • UnknownImageTypeException.java
public class UnknownImageTypeException extends Exception {
    public UnknownImageTypeException(String message) {
        super(message);
    }
}
  • ImageCompressor.java
public class ImageCompressor {

    public static final int IMAGE_TYPE_UNKNOWN = 0;
    public static final int IMAGE_TYPE_PNG = 1;
    public static final int IMAGE_TYPE_JPG = 2;

    public static int getImageType(byte[] bytes) {
        if (bytes == null) {
            return IMAGE_TYPE_UNKNOWN;
        }

        if (bytes.length >= 8
                && bytes[0] == (byte) 0x89
                && bytes[1] == (byte) 0x50
                && bytes[2] == (byte) 0x4E
                && bytes[3] == (byte) 0x47
                && bytes[4] == (byte) 0x0D
                && bytes[5] == (byte) 0x0A
                && bytes[6] == (byte) 0x1A
                && bytes[7] == (byte) 0x0A) {
            return IMAGE_TYPE_PNG;
        }

        if (bytes.length >= 3
                && bytes[0] == (byte) 0xFF
                && bytes[1] == (byte) 0xD8
                && bytes[2] == (byte) 0xFF) {
            return IMAGE_TYPE_JPG;
        }

        return IMAGE_TYPE_UNKNOWN;
    }

    public static byte[] compress(byte[] bytes, float quality) throws UnknownImageTypeException {
        int imageType = getImageType(bytes);
        if (imageType == IMAGE_TYPE_JPG) {
            return compressJpg(bytes, quality);
        } else if (imageType == IMAGE_TYPE_PNG) {
            return compressPng(bytes, quality);
        } else {
            throw new UnknownImageTypeException("未知的图片格式");
        }
    }

    public static byte[] compressJpg(byte[] bytes, float quality) {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        try {
            BufferedImage bufferedImage = ImageIO.read(byteArrayInputStream);

            // ImageIO read 方法返回 null 的情况
            // 1、图片格式不支持
            // 2、图片损坏
            if (bufferedImage == null) {
                return null;
            }

            // 开始压缩图片

            ImageWriter imageWriter = ImageIO.getImageWritersByFormatName("jpg").next();

            // 设置输出流
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ImageOutputStream imageOutputStream = ImageIO.createImageOutputStream(byteArrayOutputStream);
            imageWriter.setOutput(imageOutputStream);

            // 设置压缩参数
            JPEGImageWriteParam param = (JPEGImageWriteParam) imageWriter.getDefaultWriteParam();
            param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
            param.setCompressionQuality(quality);

            imageWriter.write(null, new IIOImage(bufferedImage, null, null), param);

            // 释放资源
            imageWriter.dispose();

            return byteArrayOutputStream.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    public static byte[] compressPng(byte[] bytes, float quality) {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        try {
            BufferedImage bufferedImage = ImageIO.read(byteArrayInputStream);

            // ImageIO read 方法返回 null 的情况
            // 1、图片格式不支持
            // 2、图片损坏
            if (bufferedImage == null) {
                return null;
            }

            // 开始压缩图片

            ImageWriter imageWriter = ImageIO.getImageWritersByFormatName("png").next();

            // 设置输出流
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ImageOutputStream imageOutputStream = ImageIO.createImageOutputStream(byteArrayOutputStream);
            imageWriter.setOutput(imageOutputStream);

            // 设置压缩参数
            ImageWriteParam param = imageWriter.getDefaultWriteParam();
            param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
            param.setCompressionType("Deflate");
            int compressionLevel = Math.min(9, Math.max(0, (int) (quality * 9)));
            param.setCompressionQuality(compressionLevel / 9.0f);

            imageWriter.write(null, new IIOImage(bufferedImage, null, null), param);

            // 释放资源
            imageWriter.dispose();

            return byteArrayOutputStream.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }
}
2、测试
FileInputStream fileInputStream = null;
byte[] bytes = null;
try {
    fileInputStream = new FileInputStream("file/compressimagetest/testPng.png");
    bytes = fileInputStream.readAllBytes();
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (fileInputStream != null) {
        try {
            fileInputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
if (bytes == null) {
    System.out.println("图片读取失败");
    return;
}

System.out.println("图片压缩前,大小:" + bytes.length);

int imageType = ImageCompressor.getImageType(bytes);
if (imageType == ImageCompressor.IMAGE_TYPE_UNKNOWN) {
    System.out.println("图片格式未知");
    return;
}

byte[] result = null;
try {
    result = ImageCompressor.compress(bytes, 0.1f);
} catch (UnknownImageTypeException e) {
    System.out.println("图片压缩失败,原因:" + e.getMessage());
    return;
}

System.out.println("图片压缩后,大小:" + result.length);

String newFileName = "file/compressimagetest/testPngCompress.";
if (imageType == ImageCompressor.IMAGE_TYPE_PNG) {
    newFileName += "png";
} else if (imageType == ImageCompressor.IMAGE_TYPE_JPG) {
    newFileName += "jpg";
}

FileOutputStream fileOutputStream = null;
try {
    fileOutputStream = new FileOutputStream(newFileName);
    fileOutputStream.write(result);
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (fileOutputStream != null) {
        try {
            fileOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  1. 测试压缩 PNG 格式图片,从 file/compressimagetest/testPng.png 读取图片,压缩后保存到 file/compressimagetest/testPngCompress.png,输出结果
图片压缩前,大小:138713
图片压缩后,大小:134312
  1. 测试压缩 JPG 格式图片,从 file/compressimagetest/testJpg.jpg 读取图片,压缩后保存到 file/compressimagetest/testJpgCompress.jpg,输出结果
图片压缩前,大小:21984
图片压缩后,大小:2381
3、小结
  1. JPEG 与 JPG 在技术上是完全相同的格式,只是文件扩展名不同

  2. PNG 采用无损压缩,JPG 采用有损压缩,PNG 的压缩效果不如 JPG 明显

  3. 有时候,图片后缀是 jpg,但是通过魔数判断,实际图片格式可能是 PNG(反之亦然)

  4. 大部分现代图片查看器、浏览器、图片处理软件都实现了格式自检测,所以修改图片后缀后,图片仍然能正常使用

  5. 缩减尺寸通常是比调整压缩参数更有效的压缩手段


Java 实现图片缩放

1、具体实现
  • UnknownImageTypeException.java
public class UnknownImageTypeException extends Exception {
    public UnknownImageTypeException(String message) {
        super(message);
    }
}
  • ImageScaler.java
public class ImageScaler {

    public static final int IMAGE_TYPE_UNKNOWN = 0;
    public static final int IMAGE_TYPE_PNG = 1;
    public static final int IMAGE_TYPE_JPG = 2;

    public static int getImageType(byte[] bytes) {
        if (bytes == null) {
            return IMAGE_TYPE_UNKNOWN;
        }

        if (bytes.length >= 8
                && bytes[0] == (byte) 0x89
                && bytes[1] == (byte) 0x50
                && bytes[2] == (byte) 0x4E
                && bytes[3] == (byte) 0x47
                && bytes[4] == (byte) 0x0D
                && bytes[5] == (byte) 0x0A
                && bytes[6] == (byte) 0x1A
                && bytes[7] == (byte) 0x0A) {
            return IMAGE_TYPE_PNG;
        }

        if (bytes.length >= 3
                && bytes[0] == (byte) 0xFF
                && bytes[1] == (byte) 0xD8
                && bytes[2] == (byte) 0xFF) {
            return IMAGE_TYPE_JPG;
        }

        return IMAGE_TYPE_UNKNOWN;
    }

    public static byte[] scale(byte[] bytes, double scale) throws UnknownImageTypeException {
        int imageType = getImageType(bytes);
        if (imageType == IMAGE_TYPE_JPG) {
            return scaleJpg(bytes, scale);
        } else if (imageType == IMAGE_TYPE_PNG) {
            return scalePng(bytes, scale);
        } else {
            throw new UnknownImageTypeException("未知的图片格式");
        }
    }

    public static byte[] scaleJpg(byte[] bytes, double scale) {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        try {
            BufferedImage srcImage = ImageIO.read(byteArrayInputStream);
            if (srcImage == null) {
                return null;
            }

            int newWidth = (int) (srcImage.getWidth() * scale);
            int newHeight = (int) (srcImage.getHeight() * scale);

            BufferedImage destImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB);

            Graphics2D g2d = destImage.createGraphics();
            g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                    RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            g2d.setRenderingHint(RenderingHints.KEY_RENDERING,
                    RenderingHints.VALUE_RENDER_QUALITY);
            g2d.drawImage(srcImage, 0, 0, newWidth, newHeight, null);
            g2d.dispose();

            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            boolean success = ImageIO.write(destImage, "jpg", byteArrayOutputStream);
            if (!success) {
                return null;
            }
            return byteArrayOutputStream.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static byte[] scalePng(byte[] bytes, double scale) {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        try {
            BufferedImage srcImage = ImageIO.read(byteArrayInputStream);
            if (srcImage == null) {
                return null;
            }

            AffineTransform transform = new AffineTransform();
            transform.scale(scale, scale);
            AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR);
            BufferedImage destImage = op.filter(srcImage, null);

            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ImageIO.write(destImage, "png", byteArrayOutputStream);
            return byteArrayOutputStream.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}
2、测试
FileInputStream fileInputStream = null;
byte[] bytes = null;
try {
    fileInputStream = new FileInputStream("file/compressimagetest/testPng.png");
    bytes = fileInputStream.readAllBytes();
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (fileInputStream != null) {
        try {
            fileInputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
if (bytes == null) {
    System.out.println("图片读取失败");
    return;
}

System.out.println("图片缩放前,大小:" + bytes.length);

int imageType = ImageScaler.getImageType(bytes);
if (imageType == ImageScaler.IMAGE_TYPE_UNKNOWN) {
    System.out.println("图片格式未知");
    return;
}

byte[] result = null;
try {
    result = ImageScaler.scale(bytes, 0.5);
} catch (UnknownImageTypeException e) {
    System.out.println("图片缩放失败,原因:" + e.getMessage());
    return;
}

System.out.println("图片缩放后,大小:" + result.length);

String newFileName = "file/compressimagetest/testPngScale.";
if (imageType == ImageScaler.IMAGE_TYPE_PNG) {
    newFileName += "png";
} else if (imageType == ImageScaler.IMAGE_TYPE_JPG) {
    newFileName += "jpg";
}

FileOutputStream fileOutputStream = null;
try {
    fileOutputStream = new FileOutputStream(newFileName);
    fileOutputStream.write(result);
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (fileOutputStream != null) {
        try {
            fileOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  1. 测试缩放 PNG 格式图片,从 file/compressimagetest/testPng.png 读取图片,缩放后保存到 file/compressimagetest/testPngScale.png,输出结果
图片缩放前,大小:138713
图片缩放后,大小:47929
  1. 测试缩放 JPG 格式图片,从 file/compressimagetest/testJpg.jpg 读取图片,缩放后保存到 file/compressimagetest/testJpgScale.jpg,输出结果
图片缩放前,大小:21984
图片缩放后,大小:2730
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐