Java 圖片處理常見需求:等比縮放、水印(圖片/文字)、裁剪。純 Java 方案 (ImageIO + BufferedImage) 部署簡單但畫質一般、大圖易 OOM;Jmagick 封裝 ImageMagick 原生庫,畫質優、支援格式多、效能強,但需部署原生庫。本文提供兩套完整工具類代碼與 Jmagick Windows 部署避坑指南。
| 維度 | 純 Java (ImageIO) | Jmagick (ImageMagick) |
|---|---|---|
| 部署複雜度 | 零依賴,跨平台 | 需安裝 ImageMagick + JNI dll/so,配置 java.library.path |
| 畫質(縮放) | 雙線性/雙三次,中等 | Lanczos/Sinc 等高級濾波器,專業級 |
| 格式支援 | JPEG/PNG/BMP/GIF/WBMP | 200+ 格式 (含 PSD、HEIC、RAW、SVG、PDF) |
| 大圖處理 | 全量載入堆內存,易 OOM | 串流處理、磁盤快取,支援 GB 級大圖 |
| 水印/特效 | 需手繪 Graphics2D | 內建 composite、annotate、morphology、distort |
| 執行緒安全 | 安全 | ImageMagick 6.x 非線程安全,需同步或用 7.x |
public static void resize(File img, int maxW, int maxH) throws IOException {
BufferedImage src = ImageIO.read(img);
int w = src.getWidth(), h = src.getHeight();
if (w <= maxW && h <= maxH) return;
double scale = Math.min((double) maxW / w, (double) maxH / h);
int nw = (int) (w * scale), nh = (int) (h * scale);
BufferedImage dst = new BufferedImage(nw, nh, BufferedImage.TYPE_3BYTE_BGR);
dst.getGraphics().drawImage(src.getScaledInstance(nw, nh, Image.SCALE_SMOOTH), 0, 0, null);
try (FileOutputStream fos = new FileOutputStream(img)) {
ImageIO.write(dst, "jpg", fos); // 或按原格式
}
}
public class ImageUtils {
static { System.setProperty("jmagick.systemclassloader", "no"); }
// 等比縮放
public static void changeSize(String src, String dst, int maxW, int maxH) {
ImageInfo info = new ImageInfo(src);
MagickImage img = new MagickImage(info);
Dimension dim = img.getDimension();
// ... 計算目標尺寸同純 Java ...
MagickImage scaled = img.scaleImage(nw, nh);
scaled.setFileName(dst);
scaled.writeImage(info);
scaled.destroyImages();
}
// 圖片水印 (右下角)
public static void watermarkImage(String src, String dst, String logo) {
MagickImage base = new MagickImage(new ImageInfo(src));
MagickImage mark = new MagickImage(new ImageInfo(logo));
mark = mark.scaleImage(base.getDimension().width/8, -1); // 等比
base.compositeImage(CompositeOperator.AtopCompositeOp, mark,
base.getDimension().width - mark.getDimension().width - 10,
base.getDimension().height - mark.getDimension().height - 10);
base.setFileName(dst);
base.writeImage(new ImageInfo());
base.destroyImages(); mark.destroyImages();
}
// 文字水印 (右下角)
public static void watermarkText(String src, String dst, String text) {
MagickImage img = new MagickImage(new ImageInfo(src));
DrawInfo di = new DrawInfo(new ImageInfo(src));
di.setFill(PixelPacket.queryColorDatabase("white"));
di.setUnderColor(new PixelPacket(0,0,0,100)); // 半透明黑底
di.setPointsize(12);
di.setTextAntialias(true);
di.setGravity(Gravity.SOUTHEAST);
di.setText(" " + text + " 上傳於 " + new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
img.annotateImage(di);
img.setFileName(dst);
img.writeImage(new ImageInfo());
img.destroyImages();
}
// 裁剪
public static void crop(String src, String dst, int x, int y, int w, int h) {
MagickImage img = new MagickImage(new ImageInfo(src));
MagickImage cropped = img.cropImage(new Rectangle(x, y, w, h));
cropped.setFileName(dst);
cropped.writeImage(new ImageInfo(src));
cropped.destroyImages(); img.destroyImages();
}
}
ImageMagick-6.4.6-4-Q16-windows-dll.exe,**勾選「Add to system path」**。*.dll 複製到 C:\Windows\System32\ (或 SysWOW64)。jmagick-win-6.3.9-q16.zip,將 jmagick.dll 複製到 System32 與 JDK/bin。jmagick.jar 放入 WEB-INF/lib 與 Tomcat/lib。catalina.bat 增加堆設定:set JAVA_OPTS=-Xms256m -Xmx768m -XX:MaxPermSize=128m (JDK 8 以前)。System.setProperty("jmagick.systemclassloader", "no"); 否則拋 UnsatisfiedLinkError。?x-oss-process=image/resize,w_200) 卸載應用層。