純 JDK 實現,無需第三方庫。核心類:ImageIO、BufferedImage、Graphics2D、ImageReader/Writer。
public class ThumbnailUtil {
private static final Logger log = LoggerFactory.getLogger(ThumbnailUtil.class);
private static final String PREFIX = "thumb_";
/**
* 生成縮略圖
* @param srcPath 源圖路徑
* @param targetWidth 目標寬度
* @param targetHeight 目標高度
* @param force 是否強制拉伸(false=保持比例縮放)
*/
public static void generate(String srcPath, int targetWidth, int targetHeight, boolean force) {
File srcFile = new File(srcPath);
if (!srcFile.exists()) {
log.warn("源文件不存在: {}", srcPath);
return;
}
try {
// 校驗格式
String format = getFormat(srcFile);
if (format == null) {
log.error("不支持的圖片格式: {}", srcPath);
return;
}
BufferedImage srcImg = ImageIO.read(srcFile);
int srcW = srcImg.getWidth();
int srcH = srcImg.getHeight();
// 計算目標尺寸
int w = targetWidth, h = targetHeight;
if (!force) {
double scaleW = (double) srcW / targetWidth;
double scaleH = (double) srcH / targetHeight;
if (scaleW < scaleH) {
if (srcW > targetWidth) {
h = (int) Math.round(srcH / scaleW);
} else { w = srcW; h = srcH; }
} else {
if (srcH > targetHeight) {
w = (int) Math.round(srcW / scaleH);
} else { w = srcW; h = srcH; }
}
}
// 繪製縮略圖
BufferedImage thumb = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics2D g = thumb.createGraphics();
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.drawImage(srcImg, 0, 0, w, h, null);
g.dispose();
// 輸出文件
String outPath = srcFile.getParent() + File.separator + PREFIX + srcFile.getName();
ImageIO.write(thumb, format, new File(outPath));
log.info("縮略圖生成成功: {}", outPath);
} catch (IOException e) {
log.error("生成失敗: {}", srcPath, e);
}
}
private static String getFormat(File file) {
String name = file.getName();
int idx = name.lastIndexOf('.');
if (idx == -1) return null;
String suffix = name.substring(idx + 1).toLowerCase();
// 校驗是否為 ImageIO 支持格式
for (String fmt : ImageIO.getReaderFormatNames()) {
if (fmt.equalsIgnoreCase(suffix)) return fmt;
}
return null;
}
}
public class CropUtil {
private static final Logger log = LoggerFactory.getLogger(CropUtil.class);
private static final String PREFIX = "crop_";
/**
* 裁剪圖片
* @param srcFile 源文件
* @param destDir 目標目錄
* @param rect 裁剪矩形(x, y, width, height)
*/
public static void crop(File srcFile, File destDir, Rectangle rect) {
if (!srcFile.exists() || !destDir.exists()) {
log.warn("文件或目錄不存在");
return;
}
try (FileInputStream fis = new FileInputStream(srcFile);
ImageInputStream iis = ImageIO.createImageInputStream(fis)) {
String format = getFormat(srcFile);
if (format == null) return;
// 獲取對應格式的 Reader
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName(format);
if (!readers.hasNext()) {
log.error("無可用 Reader: {}", format);
return;
}
ImageReader reader = readers.next();
reader.setInput(iis, true);
// 設置裁剪區域
ImageReadParam param = reader.getDefaultReadParam();
param.setSourceRegion(rect);
// 讀取並寫入
BufferedImage cropped = reader.read(0, param);
File outFile = new File(destDir, PREFIX + srcFile.getName());
ImageIO.write(cropped, format, outFile);
log.info("裁剪成功: {}", outFile.getAbsolutePath());
} catch (IOException e) {
log.error("裁剪失敗", e);
}
}
// 便捷重載
public static void crop(String srcPath, String destDir, int x, int y, int w, int h) {
crop(new File(srcPath), new File(destDir), new Rectangle(x, y, w, h));
}
private static String getFormat(File file) { /* 同 ThumbnailUtil */ }
}
前端顯示圖片常被 CSS 縮放(如 300×300 容器),實際圖片可能是 3000×2000。前端傳來的裁剪坐標是基於顯示尺寸的,需換算為原圖坐標:
// 前端傳參:displayX, displayY, displayW, displayH, displayWidth, displayHeight // 後端換算: double scaleX = (double) originalWidth / displayWidth; double scaleY = (double) originalHeight / displayHeight; int srcX = (int) Math.round(displayX * scaleX); int srcY = (int) Math.round(displayY * scaleY); int srcW = (int) Math.round(displayW * scaleX); int srcH = (int) Math.round(displayH * scaleY);
| 特性 | 自定義拖拽 DIV(推薦) | Jcrop 插件 |
|---|---|---|
| 顯示尺寸控制 | 強制統一(如 300×300),便於坐標映射 | 自適應原圖,大圖顯示醜陋,坐標映射複雜 | 參數獲取 | 手動計算,完全可控 | 內置 API 直接獲取 | 大圖處理 | 需後端先生成縮略圖作為裁剪源,裁剪後刪除臨時文件 | 同樣需處理,但插件不支持固定顯示區域 |
JDK ImageIO 提供了完整的圖片讀寫、縮放、裁剪能力。關鍵難點在於 前端顯示坐標與原圖坐標的精確換算 及 大圖處理的臨時縮略圖策略。封裝通用工具類,統一異常處理與日誌,即可滿足絕多數業務場景。