最近在做聊天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);
}
}
该文章详细描述了在开发聊天应用程序时遇到的图片上传后旋转问题以及解决方案。通过获取图片的元数据,检测并纠正图片的旋转属性,然后使用thumbnailator库进行图片压缩,确保图片在上传后能正确显示并满足大小要求。
2194

被折叠的 条评论
为什么被折叠?



