在 Java 企業級開發中,圖片處理是高頻需求:用戶頭像裁剪、商品圖縮略圖生成、版權水印添加、格式轉換等。雖然 Java AWT/ImageIO 提供了底層 API,但直接調用代碼冗長、參數繁雜、易出錯。封裝一個功能完善、參數靈活、異常處理完善的 ImageUtils 工具類,能顯著提升開發效率與代碼可維護性。
本文提供一個生產級可用的工具類完整實現,涵蓋縮放、切割、格式轉換、灰度化、文字/圖片水印六大核心功能,並附帶詳細參數說明與測試範例。
public class ImageUtils {
// 支援的圖片格式常量
public static final String IMAGE_TYPE_GIF = "gif";
public static final String IMAGE_TYPE_JPG = "jpg";
public static final String IMAGE_TYPE_JPEG = "jpeg";
public static final String IMAGE_TYPE_BMP = "bmp";
public static final String IMAGE_TYPE_PNG = "png";
public static final String IMAGE_TYPE_PSD = "psd";
// ... 方法定義
}
統一常量管理,避免硬編碼字串散落,方便擴展新格式。
scale()/**
* @param srcImageFile 源圖路徑
* @param result 目標路徑
* @param scale 縮放倍數(如 2 表示 2 倍)
* @param flag true=放大, false=縮小
*/
public static void scale(String srcImageFile, String result, int scale, boolean flag)
使用 Image.getScaledInstance() 實現,簡單高效,適合等比例縮放場景。
scale2()(支援補白)/**
* @param height 目標高度
* @param width 目標寬度
* @param bb true=保持比例並補白, false=強制拉伸
*/
public static void scale2(String srcImageFile, String result, int height, int width, boolean bb)
核心邏輯:
ratio = min(targetW/srcW, targetH/srcH),保持原圖比例。AffineTransformOp 進行高質量縮放(SCALE_SMOOTH)。bb=true 時,建立白色畫布,將縮放後圖片置中繪製,實現「含邊框的縮略圖」。cut()public static void cut(String src, String dest, int x, int y, int width, int height)
使用 CropImageFilter + FilteredImageSource 實現精確區域截取。
cut2()public static void cut2(String src, String destDir, int rows, int cols)
自動計算每塊寬高,雙層循環輸出 _r{i}_c{j}.jpg,適合大圖分片、拼圖遊戲素材生成。
cut3()public static void cut3(String src, String destDir, int destWidth, int destHeight)
根據目標塊尺寸自動推算行列數,邊緣不足一塊時自動補齊。
convert()public static void convert(String src, String formatName, String dest)
一行核心代碼:ImageIO.write(src, formatName, new File(dest))。支援 JPG↔PNG、GIF→JPG、BMP→PNG 等互轉。注意:JPG 不支援透明通道,轉換時透明區域會變黑/白。
gray()ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
ColorConvertOp op = new ColorConvertOp(cs, null);
src = op.filter(src, null);
利用 ColorConvertOp 進行色彩空間轉換,效能優於像素級手動運算。
pressText() / pressText2()/**
* @param fontName 字體名(如「微軟雅黑」)
* @param fontStyle Font.BOLD / Font.ITALIC
* @param color 字體顏色
* @param fontSize 字號
* @param x, y 位置微調偏移量
* @param alpha 透明度 0.0f~1.0f
*/
public static void pressText(String text, String src, String dest,
String fontName, int fontStyle, Color color, int fontSize,
int x, int y, float alpha)
關鍵技術點:
AlphaComposite.SRC_ATOP 實現透明度混合。getLength() 估算中英文混排寬度(中文按 2 字符計),實現文字置中。pressImage()public static void pressImage(String pressImg, String src, String dest,
int x, int y, float alpha)
將水印圖讀取為 Image,計算置中座標 (w-wm)/2, (h-hm)/2,疊加繪製。支援 PNG 透明水印(需源圖為 TYPE_INT_ARGB)。
public static void main(String[] args) {
// 1. 縮放測試
ImageUtils.scale("e:/abc.jpg", "e:/abc_scale.jpg", 2, true); // 放大 2 倍
ImageUtils.scale2("e:/abc.jpg", "e:/abc_scale2.jpg", 500, 300, true); // 限制 500x300 補白
// 2. 切割測試
ImageUtils.cut("e:/abc.jpg", "e:/abc_cut.jpg", 0, 0, 400, 400); // 截取左上 400x400
ImageUtils.cut2("e:/abc.jpg", "e:/", 2, 2); // 2x2 均分
ImageUtils.cut3("e:/abc.jpg", "e:/", 300, 300); // 300x300 分塊
// 3. 格式轉換
ImageUtils.convert("e:/abc.jpg", "GIF", "e:/abc_convert.gif");
// 4. 灰度化
ImageUtils.gray("e:/abc.jpg", "e:/abc_gray.jpg");
// 5. 文字水印
ImageUtils.pressText("我是水印", "e:/abc.jpg", "e:/abc_wm1.jpg", "微軟雅黑", Font.BOLD, Color.WHITE, 30, 0, 0, 0.5f);
ImageUtils.pressText2("另一種方式", "e:/abc.jpg", "e:/abc_wm2.jpg", "黑體", 36, Color.RED, 40, 10, 10, 0.7f);
// 6. 圖片水印
ImageUtils.pressImage("e:/logo.png", "e:/abc.jpg", "e:/abc_wm3.jpg", 0, 0, 0.6f);
}
| 問題 | 解決方案 |
|---|---|
| JPG 輸出預設無壓縮品質控制 | 引入 ImageWriteParam 設定 compressionQuality (0.7f~0.9f) |
| 大圖處理 OOM 風險 | 使用 ImageReader.setInput() 串流讀取、分塊處理;或引入 TwelveMonkeys/ImageMagick |
| 字體跨平台缺失(Linux 無微軟雅黑) | 專案打包 .ttf 字體檔,用 Font.createFont() 動態載入 |
| 水印位置支援九宮格/平鋪 | 新增 position 參數枚舉,計算座標邏輯 |
| 並發效能瓶頸 | 引入 ThreadPoolExecutor 異步處理,或使用 Thumbnailator 庫替代 |
若專案允許引入第三方庫,強烈推薦 Thumbnailator:
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.20</version>
</dependency>
// 一行代碼完成縮放+水印+輸出
Thumbnails.of("input.jpg")
.size(800, 600)
.watermark(Positions.BOTTOM_RIGHT, ImageIO.read(new File("logo.png")), 0.5f)
.outputQuality(0.85f)
.toFile("output.jpg");
優點:API 流暢、自動處理旋轉/EXIF、支援串流、記憶體優化、維護活躍。
本文提供的 ImageUtils 類雖基於原生 AWT,但功能完整、參數靈活,足以應對多數中小型專案的圖片處理需求。理解其內部原理(Graphics2D、AffineTransform、ImageFilter)有助於排查疑難雜症。對於高併發、大圖、複雜水印場景,建議遷移至 Thumbnailator 或圖片處理微服務(如基於 ImageMagick/OpenCV),實現計算與業務解耦。