Java 標準庫 javax.imageio 提供統一圖片讀寫接口。JPEG 壓縮關鍵在於 JPEGImageWriteParam 設置壓縮質量 (0.0f - 1.0f)。
import javax.imageio.*;
import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
public class JpegUtil {
/**
* 將任意圖片文件壓縮為指定質量的 JPEG
* @param srcPath 源圖片路徑 (支援 PNG, BMP, JPEG 等)
* @param destPath 目標 JPEG 路徑
* @param quality 壓縮質量 0.0f (最小/最差) ~ 1.0f (最大/最好),建議 0.7-0.85
* @throws IOException
*/
public static void compressToJpeg(String srcPath, String destPath, float quality) throws IOException {
// 1. 讀取源圖片
File srcFile = new File(srcPath);
BufferedImage srcImage = ImageIO.read(srcFile);
if (srcImage == null) {
throw new IOException("不支援的圖片格式或文件損壞: " + srcPath);
}
// 2. 若源圖有透明通道,需合成白色背景 (JPEG 不支援透明)
BufferedImage finalImage = srcImage;
if (srcImage.getColorModel().hasAlpha()) {
finalImage = new BufferedImage(
srcImage.getWidth(), srcImage.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D g = finalImage.createGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, finalImage.getWidth(), finalImage.getHeight());
g.drawImage(srcImage, 0, 0, null);
g.dispose();
}
// 3. 獲取 JPEG ImageWriter
ImageWriter writer = ImageIO.getImageWritersByFormatName("jpeg").next();
// 4. 配置壓縮參數
ImageWriteParam param = writer.getDefaultWriteParam();
if (param instanceof JPEGImageWriteParam) {
JPEGImageWriteParam jpegParam = (JPEGImageWriteParam) param;
jpegParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
jpegParam.setCompressionQuality(clamp(quality, 0.0f, 1.0f));
// 可選: 設置縮略圖、進度監聽、色度子採樣
// jpegParam.setSubsamplingMode(JPEGImageWriteParam.SUBSAMPLING_420); // 默認 4:2:0
}
// 5. 寫入輸出流
try (FileOutputStream fos = new FileOutputStream(destPath);
ImageOutputStream ios = ImageIO.createImageOutputStream(fos)) {
writer.setOutput(ios);
writer.write(null, new IIOImage(finalImage, null, null), param);
writer.dispose();
}
}
/**
* 內存中 BufferedImage 直接壓縮為 byte[] (適合上傳/網絡傳輸)
*/
public static byte[] toJpegBytes(BufferedImage image, float quality) throws IOException {
if (image.getColorModel().hasAlpha()) {
BufferedImage rgb = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D g = rgb.createGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, rgb.getWidth(), rgb.getHeight());
g.drawImage(image, 0, 0, null);
g.dispose();
image = rgb;
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ImageOutputStream ios = ImageIO.createImageOutputStream(baos)) {
ImageWriter writer = ImageIO.getImageWritersByFormatName("jpeg").next();
ImageWriteParam param = writer.getDefaultWriteParam();
if (param instanceof JPEGImageWriteParam) {
((JPEGImageWriteParam) param).setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
((JPEGImageWriteParam) param).setCompressionQuality(clamp(quality, 0.0f, 1.0f));
}
writer.setOutput(ios);
writer.write(null, new IIOImage(image, null, null), param);
writer.dispose();
}
return baos.toByteArray();
}
/**
* 生成一張測試 JPEG 圖片 (繪製幾何圖形)
*/
public static void generateTestJpeg(String destPath, int width, int height) throws IOException {
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = img.createGraphics();
// 背景漸變
g.setPaint(new GradientPaint(0, 0, Color.BLUE, width, height, Color.CYAN));
g.fillRect(0, 0, width, height);
// 繪製圓形
g.setColor(Color.WHITE);
g.setStroke(new BasicStroke(3));
g.drawOval(width/4, height/4, width/2, height/2);
// 繪製文字
g.setFont(new Font("微軟雅黑", Font.BOLD, 24));
g.drawString("Java JPEG Demo", width/2 - 80, height/2 + 10);
g.dispose();
// 直接寫入 (默認質量 ~0.75)
ImageIO.write(img, "jpeg", new File(destPath));
}
private static float clamp(float v, float min, float max) {
return Math.max(min, Math.min(max, v));
}
// 測試主方法
public static void main(String[] args) throws IOException {
// 1. 生成測試圖
String testPng = "test_source.png";
generateTestJpeg(testPng, 800, 600); // 實際生成 JPEG,這裡作為源文件演示
// 2. 壓縮為不同質量
compressToJpeg(testPng, "output_q10.jpg", 0.1f); // 極低質量,文件極小
compressToJpeg(testPng, "output_q50.jpg", 0.5f); // 中等
compressToJpeg(testPng, "output_q85.jpg", 0.85f); // 高質量,推薦 Web 使用
compressToJpeg(testPng, "output_q100.jpg", 1.0f); // 無損接近 (仍有損失)
System.out.println("壓縮完成,請對比文件大小與視覺質量。");
}
}
| 參數 | 範圍 | 影響 | 建議值 |
|---|---|---|---|
| Compression Quality | 0.0 - 1.0 | 質量越高,文件越大,視覺損失越小 | Web 圖片 0.75-0.85;存檔 0.9+ |
| Subsampling (色度子採樣) | 4:4:4 / 4:2:2 / 4:2:0 | 4:2:0 最省空間,人眼對色度不敏感 | 默認 4:2:0 即可 |
| Progressive (漸進式) | true/false | 網頁加載時先模糊後清晰,提升體驗 | Web 圖片建議 true |
// 啟用漸進式 JPEG (需 JPEGImageWriteParam 支援)
if (param instanceof JPEGImageWriteParam) {
JPEGImageWriteParam jpegParam = (JPEGImageWriteParam) param;
jpegParam.setProgressiveMode(JPEGImageWriteParam.MODE_DEFAULT); // 或 MODE_COPY_FROM_METADATA
// 自定義量化表 (極少用,專業攝影/醫療影像)
// int[] qTable = ...; jpegParam.setQTable(qTable, JPEGImageWriteParam.TABLE_LUMINANCE);
}
Font.createFont(Font.TRUETYPE_FONT, new File("msyh.ttf"))。ImageReader.setInput(..., true) 分塊讀取或用 TwelveMonkeys ImageIO 插件。Java 標準庫 ImageIO + JPEGImageWriteParam 完整支援 JPEG 讀寫與壓縮質量控制。核心要點:處理透明通道合成白底、質量參數 0.7-0.85 平衡體積清晰度、Web 場景啟用漸進式。對於高性能、大圖、現代格式 (WebP, AVIF) 需求,建議引入 TwelveMonkeys ImageIO 或使用 ProcessBuilder 調用 cwebp/imagemagick 等原生工具。