使用 Java 原生的 ImageIO、Graphics2D 实现图片任意坐标范围的裁剪( 解决图片宽高交换的问题 )

pom 依赖:

XML 复制代码
<dependency>
	<groupId>com.drewnoakes</groupId>
	<artifactId>metadata-extractor</artifactId>
	<version>2.15.0</version>
</dependency>

java 类中的 import 信息:

java 复制代码
import cn.hutool.core.codec.Base64;
import com.drew.imaging.ImageMetadataReader;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.exif.ExifIFD0Directory;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Encoder;

import javax.imageio.*;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import javax.xml.bind.DatatypeConverter;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.io.*;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
复制代码
ImageUtils.java 的裁剪图片方法:
java 复制代码
 /**
     * 裁切图片
     * @param srcImageFilePath
     * @param targetImageFilePath
     * @param x1  左上角点的 x 坐标
     * @param y1 左上角点的 y 坐标
     * @param x2 右下角点的 x 坐标
     * @param y2 右下角点的 y 坐标
     * @return
     */
    public static boolean cutImage_v1(String srcImageFilePath, String targetImageFilePath,int x1, int y1, int x2, int y2 ) {
        String srcImageFilePath_rotate_tmp = null;
        try {
            //  获取原图片的旋转角度
            int rotateAngle = ImageUtils.getRotateAngle(srcImageFilePath);
            if( rotateAngle == 90 || rotateAngle == 180 || rotateAngle == 270 ){
                String outputDirPath = MyFileUtils.createImageTmpTodayDir();
                srcImageFilePath_rotate_tmp = outputDirPath + MyIdUtils.generateuUniqueStringId() + ".jpg";
                //  有时候需要旋转90度,有时候需要旋转180度,有时候需要旋转270度
                ImageUtils.rotateImage_v1( srcImageFilePath,srcImageFilePath_rotate_tmp,Double.valueOf( rotateAngle ) );
                srcImageFilePath = srcImageFilePath_rotate_tmp;
            }

            int width = x2 - x1;
            int height = y2 - y1;
            File srcImageFile = new File(srcImageFilePath);
            String imageFormatName = ImageUtils.getImageFormatName_v1( srcImageFile );

            // 读取原始图片文件
            BufferedImage srcImage = ImageIO.read( srcImageFile );
            int type = srcImage.getType();
            int height_ = srcImage.getHeight();
            int width_ = srcImage.getWidth();
            ColorModel colorModel = srcImage.getColorModel();
            int transferType = colorModel.getTransferType();
            ColorSpace colorSpace = colorModel.getColorSpace();
            int colorSpaceType = colorSpace.getType();
            int colorSpaceNumComponents = colorSpace.getNumComponents();
            log.info( srcImageFilePath + " 的 type = " + type + ",width_ = " + width_ + ",height_ = " + height_ + ",transferType = " + transferType + ",colorSpaceType = " + colorSpaceType + ",colorSpaceNumComponents = " + colorSpaceNumComponents + ",imageFormatName = " + imageFormatName );

            // 创建 BufferedImage 对象
            // BufferedImage croppedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            BufferedImage croppedImage = new BufferedImage(width, height, type);

            // 进行剪裁操作
            Graphics2D graphics = croppedImage.createGraphics();
            graphics.drawImage(srcImage, 0, 0, width, height, x1, y1, x1 + width, y1 + height, null);
            graphics.dispose();

            // 保存剪裁后的图片文件
            ImageIO.write(croppedImage, imageFormatName, new File( targetImageFilePath ));
            return true;
        }catch ( Exception e ){
            log.error( CommonConstans.EXCEPTION_LOG_PREFIX,e );
            //  裁剪图片失败,删除目标文件
            MyFileUtils.deleteFile( targetImageFilePath );
            return false;
        }finally {
            // 无论操作成功与否,都删除临时文件
            MyFileUtils.deleteFile( srcImageFilePath_rotate_tmp );
        }
    }

    /**
     * 裁切图片
     * @param srcImageUrl
     * @param targetImageFilePath
     * @param x1  左上角点的 x 坐标
     * @param y1 左上角点的 y 坐标
     * @param x2 右下角点的 x 坐标
     * @param y2 右下角点的 y 坐标
     * @return
     */
    public static boolean cutImage_v2(String srcImageUrl, String targetImageFilePath,int x1, int y1, int x2, int y2 ) {
        String outputPath_tmp = null;
        try {
            //  将源文件下载下来生成临时文件
            String outputDir = MyFileUtils.createImageTmpTodayDir();
            outputPath_tmp = outputDir + MyIdUtils.generateuUniqueStringId() + ".jpg";
            boolean downloadSuccess = ImageUtils.imageUrl2File(srcImageUrl, outputPath_tmp);
            if( !downloadSuccess ){
                return false;
            }
            return ImageUtils.cutImage_v1( outputPath_tmp,targetImageFilePath,x1,y1,x2,y2 );
        }catch ( Exception e ){
            log.error( CommonConstans.EXCEPTION_LOG_PREFIX,e );
            //  裁剪图片失败,删除目标文件
            MyFileUtils.deleteFile( targetImageFilePath );
            return false;
        }finally {
            //  无论是否成功,都删除临时文件
            MyFileUtils.deleteFile( outputPath_tmp );
        }
    }

获取图片旋转角度的方法:

java 复制代码
  public static int getRotateAngle(String imageFilePath){
        try {
            File imageFile = new File(imageFilePath);
            Metadata metadata = ImageMetadataReader.readMetadata(imageFile);
            Directory imageDirectory = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
            int orientation = imageDirectory.getInt(ExifIFD0Directory.TAG_ORIENTATION);
            int rotateAngle = 0;
            // 原图片的方向信息
            if(6 == orientation ){
                //6旋转90
                rotateAngle = 90;
            }else if( 3 == orientation){
                //3旋转180
                rotateAngle = 180;
            }else if( 8 == orientation){
                //8旋转90
                rotateAngle = 270;
            }
            log.info( imageFilePath + " 旋转角度信息为 " + rotateAngle + " 度" );
            return rotateAngle;
        }catch ( Exception e ){
            throw new BusinessLogicException( "操作失败,检测图片旋转角度失败" );
        }
    }

旋转图片的方法:

java 复制代码
 public static void rotateImage_v1(String inputImagePath,String outputImagePath, double degree) {
        try {
            // 读取图片
            BufferedImage originalImage = ImageIO.read(new File(inputImagePath));
            String imageFormatName = getImageFormatName_v1(new File(inputImagePath));
            rotateImage_v3( originalImage,outputImagePath,degree,imageFormatName );
        }catch ( Exception e ){
            log.error( CommonConstans.EXCEPTION_LOG_PREFIX,e );
        }
    }


  public static void rotateImage_v3(BufferedImage inputImage,String outputImagePath, double degree,String savedFormatName) {
        try {
            // 计算旋转后的新宽高
            int width = inputImage.getWidth();
            int height = inputImage.getHeight();
            double sin = Math.abs(Math.sin(Math.toRadians(degree))),
                    cos = Math.abs(Math.cos(Math.toRadians(degree)));
            int newWidth = (int) Math.floor(width * cos + height * sin),
                    newHeight = (int) Math.floor(height * cos + width * sin);

            // 创建一个新的空白图片用于将原始图片绘制上去
            BufferedImage rotatedImage = new BufferedImage(newWidth, newHeight, inputImage.getType());

            // 设置旋转转换
            AffineTransform at = new AffineTransform();
            at.translate((newWidth - width) / 2, (newHeight - height) / 2);
            at.rotate(Math.toRadians(degree), width / 2, height / 2);

            // 执行旋转
            AffineTransformOp op = new AffineTransformOp(at, AffineTransformOp.TYPE_BICUBIC);
            rotatedImage = op.filter(inputImage, rotatedImage);
            if( savedFormatName == null ){
                savedFormatName = "jpg";
            }
            ImageIO.write(rotatedImage, savedFormatName, new File( outputImagePath) );
        }catch ( Exception e ){
            log.error( CommonConstans.EXCEPTION_LOG_PREFIX,e );
        }
    }

ImageUtils.java 的获取指定图片的图片格式方法:

java 复制代码
   public static String getImageFormatName_v1( File imageFile ) {
        try (ImageInputStream imageInputStream = ImageIO.createImageInputStream(imageFile)) {
            Iterator<ImageReader> imageReadersList = ImageIO.getImageReaders(imageInputStream);

            if (!imageReadersList.hasNext()) {
                log.error("Cannot detect image format." );
                return null;
            }
            ImageReader reader = imageReadersList.next();
            return reader.getFormatName();
        } catch (IOException e) {
            log.error( CommonConstans.EXCEPTION_LOG_PREFIX,e );
            throw new BusinessLogicException( "失败" );
        }
    }

下载图片的方法:

java 复制代码
 public static boolean imageUrl2File(String imageUrl, String outputPath) {
        imageUrl = MyStringUtils.null2EmptyWithTrim( imageUrl );
        outputPath = MyStringUtils.null2EmptyWithTrim( outputPath );
        if( imageUrl.length() == 0 || outputPath.length() == 0){
            return false;
        }
        InputStream in = null;
        FileOutputStream fos = null;
        try {
            URL url = new URL(imageUrl);
            URLConnection conn = url.openConnection();
            in = conn.getInputStream();
            fos = new FileOutputStream(outputPath);
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = in.read(buffer)) != -1) {
                fos.write(buffer, 0, bytesRead);
            }
            fos.flush();
            log.info( "图片 " + imageUrl + " 保存至 " + outputPath );
            return true;
        } catch (IOException e) {
            log.error( CommonConstans.EXCEPTION_LOG_PREFIX,e );
            return false;
        }finally {
            MyFileUtils.closeCloseable( fos );
            MyFileUtils.closeCloseable( in );
        }
    }

MyFileUtils.java:

java 复制代码
import java.io.*;

@Slf4j
public class MyFileUtils {

    public static void deleteFile(String filePath) {
        if( filePath == null ){
            return;
        }
        try {
            File file = new File(filePath);
            if( file.exists() ){
                file.delete();
            }
        }catch ( Exception e ){
            log.error( CommonConstans.EXCEPTION_LOG_PREFIX,e );
        }
    }
}
相关推荐
坐吃山猪18 小时前
SpringBoot01-配置文件
java·开发语言
我叫汪枫18 小时前
《Java餐厅的待客之道:BIO, NIO, AIO三种服务模式的进化》
java·开发语言·nio
yaoxtao18 小时前
java.nio.file.InvalidPathException异常
java·linux·ubuntu
Swift社区20 小时前
从 JDK 1.8 切换到 JDK 21 时遇到 NoProviderFoundException 该如何解决?
java·开发语言
DKPT20 小时前
JVM中如何调优新生代和老生代?
java·jvm·笔记·学习·spring
phltxy20 小时前
JVM——Java虚拟机学习
java·jvm·学习
seabirdssss1 天前
使用Spring Boot DevTools快速重启功能
java·spring boot·后端
喂完待续1 天前
【序列晋升】29 Spring Cloud Task 微服务架构下的轻量级任务调度框架
java·spring·spring cloud·云原生·架构·big data·序列晋升
benben0441 天前
ReAct模式解读
java·ai