做了一个图片旋转的功能,根据旋转角度自动转正,但是发现有的原图片打开就是歪了 90 度,用方法旋转了九十度之后,还是歪 90 度,旋转 180 度就是转正后再旋转 180 度
感觉就是旋转的时候好像是先默认转正了
经过查询资料发现有一种说法是:手机拍的照片,元数据会自带旋转角度,而使用原生方法或者 hutool 的旋转方法都会先转正,再进行角度旋转
原来的旋转方法
public static byte[] rotateImage(InputStream inputStream, String outputPath, Double angle) throws IOException {
if (angle == null || angle == 0) {
return IoUtil.readBytes(inputStream);
}
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
// 读取输入流中的图片,返回一个 BufferedImage 对象
BufferedImage originalImage = ImageIO.read(inputStream);
if (originalImage == null) {
// 如果无法读取到有效的图片,抛出异常
throw new IOException("无法从输入流中读取有效的图片");
}
// 获取原图片的宽度和高度
int width = originalImage.getWidth();
int height = originalImage.getHeight();
// 计算旋转后的新尺寸
double radians = Math.toRadians(angle); // 将角度转换为弧度
double sin = Math.abs(Math.sin(radians)); // 计算正弦值
double cos = Math.abs(Math.cos(radians)); // 计算余弦值
// 根据旋转角度计算新的宽度和高度
int newWidth = (int) Math.round(width * cos + height * sin);
int newHeight = (int) Math.round(width * sin + height * cos);
// 创建旋转变换
AffineTransform transform = new AffineTransform();
transform.translate(newWidth / 2.0, newHeight / 2.0); // 平移到新中心
transform.rotate(radians); // 进行旋转变换
transform.translate(-width / 2.0, -height / 2.0); // 平移回原图中心到原点
// 创建用于存放旋转后图片的 BufferedImage
BufferedImage rotatedImage = new BufferedImage(newWidth, newHeight, originalImage.getType());
// 绘制旋转后的图片
Graphics2D g = rotatedImage.createGraphics();
try {
// 设置渲染参数以提升图片质量
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); // 插值法
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); // 渲染质量
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // 抗锯齿
// 填充白色背景,背景颜色可以根据需求进行调整
g.setBackground(Color.WHITE); // 设置背景颜色为白色
g.clearRect(0, 0, newWidth, newHeight); // 清空缓冲区区域
// 应用变换并绘制原图
g.drawImage(originalImage, transform, null);
} finally {
// 释放图形上下文资源
g.dispose();
}
// 确定输出格式,默认为 png 格式
String formatName = "jpeg";
int dotIndex = outputPath.lastIndexOf('.'); // 查找文件扩展名的位置
if (dotIndex > 0) {
// 提取扩展名,作为输出格式
formatName = outputPath.substring(dotIndex + 1);
}
// 保存旋转后的图片到输出流
if (!ImageIO.write(rotatedImage, formatName, outputStream)) {
// 如果图片格式不被支持,抛出异常
throw new IOException("不支持的文件格式: " + formatName);
}
return outputStream.toByteArray();
}
}
修改后的方法
public static byte[] rotateImage(InputStream inputStream, String outputPath, Double angle) throws IOException {
if (angle == null || angle == 0) {
return IoUtil.readBytes(inputStream);
}
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
// 将 InputStream 转换为字节数组
byte[] imageBytes = toByteArray(inputStream);
// 读取 Exif Orientation
int orientation;
try (ByteArrayInputStream exifInputStream = new ByteArrayInputStream(imageBytes)) {
orientation = getExifOrientation(exifInputStream);
try(ByteArrayInputStream rotateInputStream = new ByteArrayInputStream(imageBytes)) {
Image image ;
if (orientation != 1) {
image = ImgUtil.rotate(ImageIO.read(rotateInputStream), 0);
} else {
image = ImgUtil.rotate(ImageIO.read(rotateInputStream), (int) Math.round(angle));
}
ImgUtil.write(image, FileUtil.file(outputPath));
// 确定输出格式,默认为 png 格式
String formatName = "jpeg";
int dotIndex = outputPath.lastIndexOf('.'); // 查找文件扩展名的位置
if (dotIndex > 0) {
// 提取扩展名,作为输出格式
formatName = outputPath.substring(dotIndex + 1);
}
// 保存旋转后的图片到输出流
ImgUtil.write(image, formatName, outputStream);
return outputStream.toByteArray();
}
}
} catch (Exception e) {
log.error("图片旋转失败", e);
}
return null;
}
public static int getExifOrientation(InputStream inputStream) throws Exception {
Metadata metadata = JpegMetadataReader.readMetadata(inputStream);
ExifIFD0Directory exif = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
return exif != null ? exif.getInt(ExifIFD0Directory.TAG_ORIENTATION) : 1;
}
// 将 InputStream 转换为字节数组
public static byte[] toByteArray(InputStream inputStream) throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
byte[] data = new byte[1024];
while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
buffer.flush();
return buffer.toByteArray();
}
解决思路
getExifOrientation 方法获取是不是 1 ,如果是 1 ,说明原图片不会转正再旋转,否则的话会先转正再旋转,那么可以只旋转 0 度就可以了。