Java 手机上传图片旋转解决并压缩上传笔记

最近在做聊天APP项目,通过手机上传照片,直接通过流的形式存储没问题,但是调用压缩功能后,发现压缩后的图片被旋转了如下图

 

而打开原图却是正常的:

 在网络上找了好多资料,一直梳理的不是太清楚,最终结合几个文章总结如何处理这个问题

首先,是因为有的手机,自带了一个图片旋转属性:

本人项目是通过 :MultipartFile上传文件

因此本人先将文件转成临时file:

private File transferToFile(MultipartFile multipartFile) {
//选择用缓冲区来实现这个转换即使用java 创建的临时文件 使用MultipartFile.transferto()方法 。
        File file = null;
        try {
            String originalFilename = multipartFile.getOriginalFilename();
            String[] filename = originalFilename.split("\\.");
            file=File.createTempFile(filename[0], filename[1]);
            multipartFile.transferTo(file);
            //这里我把删除临时文件去掉了,为了后面坐旋转用
            //file.deleteOnExit();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return file;
    }

然后获取图片的属性,检查是否有旋转属性,有则旋转:

注意:这里旋转的是临时图片,不是已经上传好的原图,临时图片最后要删除,保留压缩图片

/**
 * 纠正图片旋转
 *
 * @param srcFile
 */
public static void correctImg(File srcFile ) {
    FileOutputStream fos = null;
    try {
        //旋转角度
        int angle = 0;
        // 原始图片缓存
        BufferedImage srcImg = ImageIO.read(srcFile);
        int imgWidth = srcImg.getHeight();
        // 原始高度
        int imgHeight = srcImg.getWidth();
        try {
            Metadata metadata = ImageMetadataReader.readMetadata(srcFile);
            for (Directory directory: metadata.getDirectories()){
                for (Tag tag : directory.getTags()){
                    //检查确定是否有Orientation 属性,并看是否有Rotate 90 值
                    System.out.println("getDirectoryName======>"+tag.getDirectoryName()+"========getDescription=====>"+tag.getDescription());
                    if(tag.getTagName().equals("Orientation")  ){
                        //有90 复制90度
                        if(tag.getDescription().indexOf("Rotate 90") > -1){
                            angle = 90;
                        }
                        //270度,都需要旋转
                        if(tag.getDescription().indexOf("Rotate 270")>0){
                            angle = 270;
                        }
                    }
                }
                if (directory.hasErrors()) {
                    for (String error : directory.getErrors()) {
                        System.err.format("ERROR: %s", error);
                    }
                }
            }
        } catch (Exception e) {
            System.out.println(e.toString());
        }

        // 中心点位置
        double centerWidth = ((double) imgWidth) / 2;
        double centerHeight = ((double) imgHeight) / 2;

        // 图片缓存
        BufferedImage targetImg = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);

        // 旋转对应角度
        Graphics2D g = targetImg.createGraphics();
        g.rotate(Math.toRadians(angle), centerWidth, centerHeight);
        g.drawImage(srcImg, (imgWidth - srcImg.getWidth()) / 2, (imgHeight - srcImg.getHeight()) / 2, null);
        g.rotate(Math.toRadians(-angle), centerWidth, centerHeight);
        g.dispose();

        // 输出图片
        fos = new FileOutputStream(srcFile);
        ImageIO.write(targetImg, "jpg", fos);

        System.out.println("图片旋转==>完成!");
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (fos != null) {
            try {
                fos.flush();
                fos.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

然后调用压缩图片:

/**
     * 根据指定大小和指定精度压缩图片
     *
     * @param srcPath      源图片地址
     * @param desPath      目标图片地址
     * @param desFileSize  指定图片大小,单位kb(压缩到多大以内)
     * @param accuracy     精度,递归压缩的比率,建议小于0.9
     * @param desMaxWidth  目标最大宽度
     * @param desMaxHeight 目标最大高度
     * @return 目标文件路径
     */
    public static String compressPicForScale(String srcPath, String desPath, long desFileSize, double accuracy, int desMaxWidth, int desMaxHeight) {
        if (!new File(srcPath).exists()) {
            return null;
        }
        try {
            File srcFile = new File(srcPath);
            long srcFileSize = srcFile.length();
            System.out.println("源图片:" + srcPath + ",大小:" + srcFileSize / 1024 + "kb");
            //获取图片信息
            BufferedImage bim = ImageIO.read(srcFile);
            int srcWidth = bim.getWidth();
            int srcHeight = bim.getHeight();

            //先转换成jpg
            Thumbnails.Builder<File> builder = Thumbnails.of(srcFile).outputFormat("jpg");

            // 指定大小(宽或高超出会才会被缩放)
            if (srcWidth > desMaxWidth || srcHeight > desMaxHeight) {
                builder.size(desMaxWidth, desMaxHeight);
            } else {
                //宽高均小,指定原大小
                builder.size(srcWidth, srcHeight);
            }

            // 写入到内存
            ByteArrayOutputStream bos = new ByteArrayOutputStream(); //字节输出流(写入到内存)
            builder.toOutputStream(bos);

            // 获取偏转角度
//            int angle = getAngle(srcFile);
//            System.out.println("目标图片偏转角度111:" + angle);

            // 递归压缩,直到目标文件大小小于desFileSize
            byte[] bytes = compressPicCycle(bos.toByteArray(), desFileSize, accuracy);

            // 输出到文件
            File desFile = new File(desPath);
            FileOutputStream fos = new FileOutputStream(desFile);
            fos.write(bytes);
            fos.close();

//            //调用旋转检查
//            correctImg(desPath);
            System.out.println("目标图片:" + desPath + ",大小" + desFile.length() / 1024 + "kb");
            System.out.println("图片压缩完成!");
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return desPath;
    }

最后删除临时图片:

//删除临时
f.deleteOnExit();

这样就实现了图片的旋转判断,并更正,再压缩保存

其中引入的pom:

<!-- 图片压缩 -->
<dependency>
    <groupId>net.coobird</groupId>
    <artifactId>thumbnailator</artifactId>
    <version>0.4.8</version>
</dependency>
<dependency>
    <groupId>com.drewnoakes</groupId>
    <artifactId>metadata-extractor</artifactId>
    <version>2.7.0</version>
</dependency>
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.10.1</version>
</dependency>
<dependency>
    <groupId>com.drewnoakes</groupId>
    <artifactId>metadata-extractor</artifactId>
    <version>2.18.0</version>
</dependency>

以下是全代码,入需要保存,自己增加上传文件保存内容

package com.xb.chat.utils;

import com.drew.imaging.ImageMetadataReader;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.Tag;
import net.coobird.thumbnailator.Thumbnails;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.math.BigDecimal;

public class PicYS {

    private void picUplod(MultipartFile uploadFile){
        //文件路径
        String upLoadPath = "/data/img/";
        //图片名自定义
        String ysfileName = "_yasuo.jpg";
        //因为有的手机图片是带旋转属性,所以,可以用临时文件方法,先把图片上传,并调整正确位置,再进行压缩,压缩完毕后再删除临时文件的图
        File f = this.transferToFile(uploadFile);

        try {
            //旋转
            this.correctImg(f);
            //压缩图片
            this.compressPicForScale(f.getCanonicalPath(), upLoadPath + ysfileName,
                    100, 0.8, 200, 200); // 图片小于1000kb
            //删除临时
            f.deleteOnExit();
        } catch (Exception e) {
            System.out.println(e.toString());
        }
    }


    private File transferToFile(MultipartFile multipartFile) {
//        选择用缓冲区来实现这个转换即使用java 创建的临时文件 使用 MultipartFile.transferto()方法 。
        File file = null;
        try {
            String originalFilename = multipartFile.getOriginalFilename();
            String[] filename = originalFilename.split("\\.");
            file=File.createTempFile(filename[0], filename[1]);
            multipartFile.transferTo(file);
            //file.deleteOnExit();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return file;
    }


    /**
     * 纠正图片旋转
     *
     * @param srcFile
     */
    public static void correctImg(File srcFile ) {
        FileOutputStream fos = null;
        try {
            //旋转角度
            int angle = 0;
            // 原始图片缓存
            BufferedImage srcImg = ImageIO.read(srcFile);
            int imgWidth = srcImg.getHeight();
            // 原始高度
            int imgHeight = srcImg.getWidth();
            try {
                Metadata metadata = ImageMetadataReader.readMetadata(srcFile);
                for (Directory directory: metadata.getDirectories()){
                    for (Tag tag : directory.getTags()){
                        //检查确定是否有Orientation 属性,并看是否有Rotate 90 值
                        System.out.println("getDirectoryName======>"+tag.getDirectoryName()+"========getDescription=====>"+tag.getDescription());
                        if(tag.getTagName().equals("Orientation")  ){
                            //有90 复制90度
                            if(tag.getDescription().indexOf("Rotate 90") > -1){
                                angle = 90;
                            }
                            //270度,都需要旋转
                            if(tag.getDescription().indexOf("Rotate 270")>0){
                                angle = 270;
                            }
                        }
                    }
                    if (directory.hasErrors()) {
                        for (String error : directory.getErrors()) {
                            System.err.format("ERROR: %s", error);
                        }
                    }
                }
            } catch (Exception e) {
                System.out.println(e.toString());
            }

            // 中心点位置
            double centerWidth = ((double) imgWidth) / 2;
            double centerHeight = ((double) imgHeight) / 2;

            // 图片缓存
            BufferedImage targetImg = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);

            // 旋转对应角度
            Graphics2D g = targetImg.createGraphics();
            g.rotate(Math.toRadians(angle), centerWidth, centerHeight);
            g.drawImage(srcImg, (imgWidth - srcImg.getWidth()) / 2, (imgHeight - srcImg.getHeight()) / 2, null);
            g.rotate(Math.toRadians(-angle), centerWidth, centerHeight);
            g.dispose();

            // 输出图片
            fos = new FileOutputStream(srcFile);
            ImageIO.write(targetImg, "jpg", fos);

            System.out.println("图片旋转==>完成!");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.flush();
                    fos.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 根据指定大小和指定精度压缩图片
     *
     * @param srcPath      源图片地址
     * @param desPath      目标图片地址
     * @param desFileSize  指定图片大小,单位kb(压缩到多大以内)
     * @param accuracy     精度,递归压缩的比率,建议小于0.9
     * @param desMaxWidth  目标最大宽度
     * @param desMaxHeight 目标最大高度
     * @return 目标文件路径
     */
    public static String compressPicForScale(String srcPath, String desPath, long desFileSize, double accuracy, int desMaxWidth, int desMaxHeight) {
        if (!new File(srcPath).exists()) {
            return null;
        }
        try {
            File srcFile = new File(srcPath);
            long srcFileSize = srcFile.length();
            System.out.println("源图片:" + srcPath + ",大小:" + srcFileSize / 1024 + "kb");
            //获取图片信息
            BufferedImage bim = ImageIO.read(srcFile);
            int srcWidth = bim.getWidth();
            int srcHeight = bim.getHeight();

            //先转换成jpg
            Thumbnails.Builder<File> builder = Thumbnails.of(srcFile).outputFormat("jpg");

            // 指定大小(宽或高超出会才会被缩放)
            if (srcWidth > desMaxWidth || srcHeight > desMaxHeight) {
                builder.size(desMaxWidth, desMaxHeight);
            } else {
                //宽高均小,指定原大小
                builder.size(srcWidth, srcHeight);
            }

            // 写入到内存
            ByteArrayOutputStream bos = new ByteArrayOutputStream(); //字节输出流(写入到内存)
            builder.toOutputStream(bos);

            // 获取偏转角度
//            int angle = getAngle(srcFile);
//            System.out.println("目标图片偏转角度111:" + angle);

            // 递归压缩,直到目标文件大小小于desFileSize
            byte[] bytes = compressPicCycle(bos.toByteArray(), desFileSize, accuracy);

            // 输出到文件
            File desFile = new File(desPath);
            FileOutputStream fos = new FileOutputStream(desFile);
            fos.write(bytes);
            fos.close();

//            //调用旋转检查
//            correctImg(desPath);
            System.out.println("目标图片:" + desPath + ",大小" + desFile.length() / 1024 + "kb");
            System.out.println("图片压缩完成!");
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return desPath;
    }

    private static byte[] compressPicCycle(byte[] bytes, long desFileSize, double accuracy) throws IOException {
        // File srcFileJPG = new File(desPath);
        long srcFileSizeJPG = bytes.length;
        // 2、判断大小,如果小于500kb,不压缩;如果大于等于500kb,压缩
        if (srcFileSizeJPG <= desFileSize * 1024) {
            return bytes;
        }
        // 计算宽高
        BufferedImage bim = ImageIO.read(new ByteArrayInputStream(bytes));
        int srcWidth = bim.getWidth();
        int srcHeight = bim.getHeight();
        int desWidth = new BigDecimal(srcWidth).multiply(new BigDecimal(accuracy)).intValue();
        int desHeight = new BigDecimal(srcHeight).multiply(new BigDecimal(accuracy)).intValue();

        ByteArrayOutputStream bos = new ByteArrayOutputStream(); //字节输出流(写入到内存)
        Thumbnails.of(new ByteArrayInputStream(bytes)).size(desWidth, desHeight).outputQuality(accuracy).toOutputStream(bos);
        return compressPicCycle(bos.toByteArray(), desFileSize, accuracy);
    }
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
文件上传是Web开发中常见的功能之一,Java中也提供了多种方式来实现文件上传。其中,一种常用的方式是通过Apache的commons-fileupload组件来实现文件上传。 以下是实现文件上传的步骤: 1.在pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.3</version> </dependency> ``` 2.在前端页面中添加文件上传表单: ```html <form method="post" enctype="multipart/form-data" action="upload"> <input type="file" name="file"> <input type="submit" value="Upload"> </form> ``` 3.在后台Java代码中处理上传文件: ```java // 创建一个DiskFileItemFactory对象,用于解析上传的文件 DiskFileItemFactory factory = new DiskFileItemFactory(); // 设置缓冲区大小,如果上传的文件大于缓冲区大小,则先将文件保存到临时文件中,再进行处理 factory.setSizeThreshold(1024 * 1024); // 创建一个ServletFileUpload对象,用于解析上传的文件 ServletFileUpload upload = new ServletFileUpload(factory); // 设置上传文件的大小限制,这里设置为10MB upload.setFileSizeMax(10 * 1024 * 1024); // 解析上传的文件,得到一个FileItem的List集合 List<FileItem> items = upload.parseRequest(request); // 遍历FileItem的List集合,处理上传的文件 for (FileItem item : items) { // 判断当前FileItem是否为上传的文件 if (!item.isFormField()) { // 获取上传文件的文件名 String fileName = item.getName(); // 创建一个File对象,用于保存上传的文件 File file = new File("D:/uploads/" + fileName); // 将上传的文件保存到指定的目录中 item.write(file); } } ``` 以上代码中,首先创建了一个DiskFileItemFactory对象,用于解析上传的文件。然后设置了缓冲区大小和上传文件的大小限制。接着创建一个ServletFileUpload对象,用于解析上传的文件。最后遍历FileItem的List集合,判断当前FileItem是否为上传的文件,如果是,则获取文件名,创建一个File对象,将上传的文件保存到指定的目录中。 4.文件上传完成后,可以给用户一个提示信息,例如: ```java response.getWriter().write("File uploaded successfully!"); ``` 以上就是使用Apache的commons-fileupload组件实现文件上传的步骤。需要注意的是,文件上传可能会带来安全隐患,因此在处理上传的文件时,需要进行严格的校验和过滤。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值