使用 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 );
        }
    }
}
相关推荐
xmh-sxh-131411 分钟前
jdk各个版本介绍
java
天天扭码30 分钟前
五天SpringCloud计划——DAY2之单体架构和微服务架构的选择和转换原则
java·spring cloud·微服务·架构
程序猿进阶31 分钟前
堆外内存泄露排查经历
java·jvm·后端·面试·性能优化·oom·内存泄露
FIN技术铺35 分钟前
Spring Boot框架Starter组件整理
java·spring boot·后端
小曲程序42 分钟前
vue3 封装request请求
java·前端·typescript·vue
陈王卜1 小时前
django+boostrap实现发布博客权限控制
java·前端·django
小码的头发丝、1 小时前
Spring Boot 注解
java·spring boot
java亮小白19971 小时前
Spring循环依赖如何解决的?
java·后端·spring
飞滕人生TYF1 小时前
java Queue 详解
java·队列
武子康2 小时前
大数据-230 离线数仓 - ODS层的构建 Hive处理 UDF 与 SerDe 处理 与 当前总结
java·大数据·数据仓库·hive·hadoop·sql·hdfs