首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 圖片裁剪與縮略圖生成完整實戰:ImageIO 核心 API 深度應用

     編輯:Java 相關知識

核心類與依賴

純 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 直接獲取
大圖處理需後端先生成縮略圖作為裁剪源,裁剪後刪除臨時文件同樣需處理,但插件不支持固定顯示區域

最佳實踐流程

  1. 上傳原圖 → 保存
  2. 後端生成固定尺寸(如 300×300)縮略圖 → 返回前端顯示
  3. 前端在縮略圖上拖拽選區 → 獲取顯示坐標
  4. 後端接收坐標 → 換算為原圖坐標 → 裁剪原圖 → 保存結果
  5. 刪除臨時縮略圖

總結

JDK ImageIO 提供了完整的圖片讀寫、縮放、裁剪能力。關鍵難點在於 前端顯示坐標與原圖坐標的精確換算 及 大圖處理的臨時縮略圖策略。封裝通用工具類,統一異常處理與日誌,即可滿足絕多數業務場景。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved