Java 对图片进行上传或下载后发生了90度的旋转

news2024/9/30 21:22:59

一、背景介绍

在开发给上传图片打水印的时候,发现了一个奇怪的事情。某张图片在上传后发生了90度的旋转,但是在window打开来是竖的,上传后在打开就是横的。后来上网查询是由于手机在拍摄时候是横着拍的,在图片处理时将旋转角度存储在了exif信息中,但是用Java方法读取图片会忽略旋转角度,导致了这个问题,而window图片预览、手机图片查看等都会根据旋转角度进行调整,所以是没有问题的。

二、获取旋转角度

那么我们如何才能获取到图片真实的旋转角度呢,这里就需要依赖一个第三方jar包:metadata-extractor,我们可以通过这个jar包获取到照片中包含的EXIF信息,在其中就有着我们需要的旋转角度。

使用到的jar包:

<dependency>
    <groupId>com.drewnoakes</groupId>
    <artifactId>metadata-extractor</artifactId>
    <version>2.7.0</version>
</dependency>

打印出EXIF信息:

// 遍历打印图片文件的EXIF信息
private static void printExif(File file) throws ImageProcessingException, IOException {
    Metadata metadata = ImageMetadataReader.readMetadata(file);
    for (Directory directory : metadata.getDirectories()) {
        for (Tag tag : directory.getTags()) {
            System.out.println(tag + " = " + tag.getDescription());
        }
    }
}

下面是打印出的EXIF内容
在这里插入图片描述

我们可以看到在打印出的信息中有这么一行信息,这行信息中的Rotate 90 CW就表示这张照片的旋转角度是90度。

[Exif IFD0] Orientation - Right side, top (Rotate 90 CW) = Right side, top (Rotate 90 CW)

那不同的拍照角度分别对应着什么标识符呢,下面是我从网上找来的大神的实验结果,以供参考:

  • 横拿手机右手拍照,照片方向"1"“Horizontal”。
  • 正常拿手机竖拍,照片方向"6"“Rotate 90 CW”,图片顺时针旋转90度时,即正常。
  • 再转90度,横拿,左手拍照,照片方向"3"“Rotate 180”,旋转180度即可正常显示方向。
  • 再转90度,手机头朝下拍,照片方向"8"“Rotate 270 CW”。

三、纠正图片

在上面我们获取到了图片的旋转角度后,我们就可以通过下面的代码从而获取旋转后的图片了。

/**
 * 纠正图片旋转
 *
 * @param srcImgPath
 */
public static void correctImg(String srcImgPath) {
    FileOutputStream fos = null;
    try {
        // 原始图片
        File srcFile = new File(srcImgPath);

        // 获取偏转角度
        int angle = getAngle(srcFile);
        if (angle != 90 && angle != 270) {
            return;
        }

        // 原始图片缓存
        BufferedImage srcImg = ImageIO.read(srcFile);

        // 宽高互换
        // 原始宽度
        int imgWidth = srcImg.getHeight();
        // 原始高度
        int imgHeight = srcImg.getWidth();

        // 中心点位置
        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);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (fos != null) {
            try {
                fos.flush();
                fos.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

/**
 * 获取图片旋转角度
 *
 * @param file 上传图片
 * @return
 */
private static int getAngle(File file) throws Exception {
    Metadata metadata = ImageMetadataReader.readMetadata(file);
    for (Directory directory : metadata.getDirectories()) {
        for (Tag tag : directory.getTags()) {
            if ("Orientation".equals(tag.getTagName())) {
                String orientation = tag.getDescription();
                if (orientation.contains("90")) {
                    return 90;
                } else if (orientation.contains("180")) {
                    return 180;
                } else if (orientation.contains("270")) {
                    return 270;
                }
            }
        }
    }
    return 0;
}

转载自:https://blog.csdn.net/yuyu1067/article/details/116333935

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/919864.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

postman接口参数化设置

为什么需要参数化&#xff1f; 我们在做接口测试的过程中&#xff0c;会遇到需要测试同一个接口使用不同的数据的情况&#xff0c;如果每次去一个个填写数据就太麻烦了&#xff0c;这时我们就需要用到接口参数化&#xff0c;我们把数据单独的存放在一个文件中管理&#xff0c;…

一生一芯8——在github上添加ssh key

为在github上下载代码框架&#xff0c;这里在github上使用ssh key进行远程连接&#xff0c;方便代码拉取 参照博客https://blog.csdn.net/losthief/article/details/131502734 本机 系统ubuntu22.04 git 版本2.34.1 本人是第一次配置&#xff0c;没有遇到奇奇怪怪的错误&…

AI绘画:SDXL版ControlNet模型和使用方法!

SDXL是目前最强的AI绘画基础模型&#xff0c;直接加载模型&#xff0c;就可以生成不错的效果。但是它有一个致命的问题&#xff0c;就是不支持ControlNet。 在AI绘画中&#xff0c;ControlNet是一个非常重要的工具。有了它&#xff0c;就可以生成更加可控精准的图片。ControlN…

贪心算法:简单而高效的优化策略

在计算机科学中&#xff0c;贪心算法是一种简单而高效的优化策略&#xff0c;用于解决许多组合优化问题。虽然它并不适用于所有问题&#xff0c;但在一些特定情况下&#xff0c;贪心算法能够产生近似最优解&#xff0c;而且计算成本较低。在本文中&#xff0c;我们将深入探讨贪…

linux————LVS集群

目录 一、集群概述 一、负载均衡技术类型 二、负载均衡实现方式 二、LVS结构 一、三层结构 二、架构对象 三、LVS工作模式 四、负载均衡算法 一、静态负载均衡 二、动态负载 五、ipvsadm命令详解 六、LVS配置 一、基础配置 二、实现NAT模型搭建 配置IP地址 安装…

Python入门教程 | Python3 基本数据类型

赋值 Python 中的变量不需要声明。每个变量在使用前都必须赋值&#xff0c;变量赋值以后该变量才会被创建。 在 Python 中&#xff0c;变量就是变量&#xff0c;它没有类型&#xff0c;我们所说的"类型"是变量所指的内存中对象的类型。 等号&#xff08;&#xff…

MyBatid动态语句且模糊查询

目录 什么是MyBtais动态语句&#xff1f;&#xff1f;&#xff1f; MyBatis常用的动态标签和表达式 if标签 Choose标签 where标签 MyBatis模糊查询 #与$的区别 ​编辑 MyBatis映射 resultType resultMap 什么是MyBtais动态语句&#xff1f;&#xff1f;&#xff1f;…

LeetCode面试经典150题(day 1)

LeetCode是一个免费刷题的一个网站&#xff0c;想要通过笔试的小伙伴可以每天坚持刷两道算法题。 接下来&#xff0c;每天我将更新LeetCode面试经典150题的其中两道算法题&#xff0c;一边巩固自己&#xff0c;一遍希望能帮助到有需要的小伙伴。 88.合并两个有序数组 给你两个…

CGAL 网格(Mesh)数据骨架提取

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 骨架是一种非常有效的形状抽象,其被广泛的用于分割、形状匹配、曲面重建、虚拟导航等领域。正如名称所示,一条曲线骨架本质上是曲线线性化的图结构,并且它不是由曲面(2D)组成的3D几何体的中轴线。 如下图所示,形…

BM2 链表内指定区间反转,为什么链表要new一个结点?

链表内指定区间反转_牛客题霸_牛客网 (nowcoder.com) 思路就是&#xff0c;把需要反转的结点放入栈中&#xff0c;然后在弹出来。 /*** struct ListNode {* int val;* struct ListNode *next;* ListNode(int x) : val(x), next(nullptr) {}* };*/#include<stack> class…

7-42 整型关键字的散列映射

题目链接&#xff1a;这里 题目大意&#xff1a;就是写一个线性探测的散列 然鹅&#xff0c;我不会写(?)我一共错了两个地方 有冲突的情况下&#xff0c;就是线性探查然后往后找&#xff0c;但是我之前写的是t&#xff0c;应该是t (t1)%p;…在有重复关键字的时候&#xff0c…

Android studio 2022.3.1 鼠标移动时不显示快速文档

在使用技术工具的过程中&#xff0c;我们时常会遇到各种各样的问题和挑战。最近&#xff0c;我升级了我的Android Studio到2022.3.1版本&#xff0c;但是在使用过程中&#xff0c;我碰到了一个让我颇为困扰的问题&#xff1a;在鼠标移动到类名或字段上时&#xff0c;原本应该显…

不同规模的测试团队分别适合哪些测试用例管理工具?测试用例管理工具选型指南

随着软件系统规模的持续增大&#xff0c;业务复杂度的持续增加&#xff0c;软件测试的复杂度也随之越来越大。软件测试工作的复杂性主要体现在测试用例的编写、维护、执行和管理方面。而创建易于阅读、维护和管理的测试用例能够显著减轻测试工作的复杂性。 本篇文章将较为系统的…

SpringBoot Cache

一、基本概念 Spring Cache 是一个框架&#xff0c;实现了基于注解的缓存功能&#xff0c;只需要简单地加一个注解&#xff0c;就能实现缓存功能。 Spring Cache 提供了一层抽象&#xff0c;底层可以切换不同的缓存实现&#xff0c;例如&#xff1a; • EHCache • Caffeine …

回归预测 | MATLAB实现SA-ELM模拟退火算法优化极限学习机多输入单输出回归预测(多指标,多图)

回归预测 | MATLAB实现SA-ELM模拟退火算法优化极限学习机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现SA-ELM模拟退火算法优化极限学习机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09;效果一览基本…

裂缝检测,只依赖OPENCV,基于YOLO8S

裂缝检测&#xff0c;只依赖OPENCV&#xff0c;YOLOV8S 现在YOLOV8S训练目标非常方便&#xff0c;可以直接转换成ONNX让OPENCV调用&#xff0c;支持C/PYTHON&#xff0c;原理很简单&#xff0c;自己找博客&#xff0c;有兴趣相互交流

爆肝spring源码笔记

1.总览 首先学习spring源码 的大纲如下 图1.1为主要学习 内容其中 容器 AOP占百分之六七十 然后学完sping源码 再学springmvc源码就简单很多 图1.2中指出了springmvc中父子工厂的事务冲突问题 这个在springboot中由于引入了内置的 tomcat后解决 后面会讲 然后sprin…

在当今信息化社会中的安全大文件传输

随着科技的不断进步&#xff0c;数据已经成为各个领域和行业的宝贵财富。然而&#xff0c;随之而来的数据传输和交换问题也成为一个日益突出的挑战。在这篇文章中&#xff0c;我们将探讨在当今信息化社会中的安全大文件传输的重要性&#xff0c;以及如何应对传统传输方式所面临…

工程管理与工作流

1 统一开发环境/ 协作工具 你知道开发环境指的是什么吗&#xff1f; 开发环境&#xff1a; 工程运行环境、开发工具/ 编辑器 、开发依赖环境、 配置文件 软件环境&#xff1a; “仿真预演”环境 Staging 生产环境前最终验证、 这一环境尽可能的仿真了真实的生产环境 、另一个…

Django REST framework实现api接口

drf 是Django REST framework的简称&#xff0c;drf 是基于django的一个api 接口实现框架&#xff0c;REST是接口设计的一种风格。 一、 安装drf pip install djangorestframework pip install markdown # Markdown support for the browsable API. pip install …