修改描述

dev
zhaokai 2026-03-11 16:59:59 +08:00
parent 36f8af6a76
commit 473998f9ce
2 changed files with 198 additions and 94 deletions

View File

@ -932,7 +932,7 @@ public class ArchivesQueryExe {
}
if(!ObjectUtils.isEmpty(item.getUserAvatarUrl())){
// PictureRenderData facePicture = Pictures.ofUrl(fileUrlConfig.getPrefixUrl() + item.getUserAvatarUrl(), PictureType.JPEG).size(50, 50).style("rotation:90;").create();//网络图片地址
PictureRenderData facePicture = ImageUtil.createWithThumbnailator(fileUrlConfig.getPrefixUrl() + item.getUserAvatarUrl(), 50, 50);
PictureRenderData facePicture = ImageUtil.createWithThumbnailator(fileUrlConfig.getPrefixUrl() + item.getUserAvatarUrl(), 100, 100);
itemMap.put("facePicture", facePicture);
}

View File

@ -9,6 +9,7 @@ import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@ -27,11 +28,8 @@ public class FixedPictureUtils {
* @throws IOException IO
*/
public static PictureRenderData ofNormalUrl(String imgUrl, int width, int height) throws IOException {
// 1. 从URL读取图片字节纯JDK8原生
byte[] imgBytes = readBytesFromUrl(imgUrl);
// 2. 修复图片旋转核心移除containsTree()依赖)
byte[] fixedImgBytes = fixImageRotationForJdk8(imgBytes);
// 3. 构建PictureRenderData使用ofBytes方法
byte[] fixedImgBytes = fixImageRotation(imgBytes);
return Pictures.ofBytes(fixedImgBytes)
.size(width, height)
.create();
@ -44,7 +42,7 @@ public class FixedPictureUtils {
URL picUrl = new URL(url);
try (InputStream is = picUrl.openStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
byte[] buffer = new byte[1024];
byte[] buffer = new byte[4096];
int len;
while ((len = is.read(buffer)) != -1) {
baos.write(buffer, 0, len);
@ -54,101 +52,141 @@ public class FixedPictureUtils {
}
/**
* EXIFcontainsTree()
* JDK 8
*/
private static byte[] fixImageRotationForJdk8(byte[] imageBytes) throws IOException {
private static byte[] fixImageRotation(byte[] imageBytes) throws IOException {
// 1. 解析EXIF旋转角度
int rotation = getExifRotation(imageBytes);
// 2. 兜底检测无EXIF时自动判断
if (rotation == 0) {
rotation = detectImageOrientation(imageBytes);
}
// 3. 无需旋转则返回原字节
if (rotation == 0) {
return imageBytes;
}
// 4. 读取并旋转图片
BufferedImage original = readImage(imageBytes);
BufferedImage rotated = rotateImage(original, rotation);
// 5. 转为字节返回
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
String format = getImageFormat(imageBytes);
ImageIO.write(rotated, format, baos);
return baos.toByteArray();
}
}
/**
* JDK 8EXIFswitch
*/
private static int getExifRotation(byte[] imageBytes) {
try (ByteArrayInputStream bais = new ByteArrayInputStream(imageBytes);
ImageInputStream iis = ImageIO.createImageInputStream(bais)) {
// 获取图片读取器
Iterator<ImageReader> readers = ImageIO.getImageReaders(iis);
if (!readers.hasNext()) {
return imageBytes; // 无读取器,返回原字节
return 0;
}
ImageReader reader = readers.next();
reader.setInput(iis, true);
// 1. 获取旋转角度绕开containsTree()
int rotation = getExifRotationWithoutContainsTree(reader);
if (rotation == 0) {
reader.dispose();
return imageBytes; // 无需旋转,直接返回
}
// 2. 读取原始图片
BufferedImage original = reader.read(0);
reader.dispose();
// 3. 旋转图片
BufferedImage rotated = rotateImageForJdk8(original, rotation);
// 4. 转为字节返回
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
String format = getImageFormatForJdk8(imageBytes);
ImageIO.write(rotated, format, baos);
return baos.toByteArray();
}
}
}
/**
* containsTree()EXIF
*/
private static int getExifRotationWithoutContainsTree(ImageReader reader) {
try {
IIOMetadata metadata = reader.getImageMetadata(0);
String exifFormat = "javax_imageio_jpeg_image_1.0";
int rotation = 0;
// 直接尝试获取元数据树不先判断containsTree()
// 捕获异常即可,无需提前检查
Object metadataTree;
// 尝试所有支持的EXIF格式
String[] formats = {"javax_imageio_jpeg_image_1.0", "javax_imageio_tiff_image_1.0"};
for (String format : formats) {
try {
metadataTree = metadata.getAsTree(exifFormat);
} catch (IllegalArgumentException e) {
// 不支持该格式返回0度
return 0;
}
if (!(metadataTree instanceof IIOMetadataNode)) {
return 0;
}
IIOMetadataNode root = (IIOMetadataNode) metadataTree;
IIOMetadataNode exifNode = getChildNodeForJdk8(root, "app0JFIF");
if (exifNode == null) {
exifNode = getChildNodeForJdk8(root, "exif");
}
if (exifNode == null) return 0;
// 解析Orientation参数
String orientation = exifNode.getAttribute("Orientation");
if (orientation == null || orientation.trim().isEmpty()) return 0;
int ori = Integer.parseInt(orientation);
switch (ori) {
case 3: return 180;
case 6: return 90;
case 8: return 270;
default: return 0;
IIOMetadataNode root = (IIOMetadataNode) metadata.getAsTree(format);
rotation = parseOrientationFromMetadata(root);
if (rotation != 0) {
break;
}
} catch (Exception e) {
// 任何异常都返回0度比如元数据解析失败
continue;
}
}
reader.dispose();
return rotation;
} catch (Exception e) {
return 0;
}
}
/**
* JDK8XML
* JDK 8Orientation使switch
*/
private static IIOMetadataNode getChildNodeForJdk8(IIOMetadataNode parent, String name) {
private static int parseOrientationFromMetadata(IIOMetadataNode root) {
IIOMetadataNode node = findNodeRecursive(root, "Orientation");
if (node == null) {
return 0;
}
String orientation = node.getAttribute("value");
if (orientation == null || orientation.isEmpty()) {
orientation = node.getTextContent();
}
if (orientation == null || orientation.isEmpty()) {
return 0;
}
int ori = Integer.parseInt(orientation);
// JDK 8支持的switch语句替换表达式
int rotation = 0;
switch (ori) {
case 1:
rotation = 0;
break;
case 2:
rotation = 0;
break;
case 3:
rotation = 180;
break;
case 4:
rotation = 180;
break;
case 5:
rotation = 90;
break;
case 6:
rotation = 90;
break;
case 7:
rotation = 270;
break;
case 8:
rotation = 270;
break;
default:
rotation = 0;
break;
}
return rotation;
}
/**
* JDK 8
*/
private static IIOMetadataNode findNodeRecursive(IIOMetadataNode parent, String nodeName) {
if (parent.getNodeName().equals(nodeName)) {
return parent;
}
for (int i = 0; i < parent.getLength(); i++) {
Object item = parent.item(i);
if (item instanceof IIOMetadataNode) {
IIOMetadataNode node = (IIOMetadataNode) item;
if (name.equals(node.getNodeName())) {
return node;
IIOMetadataNode child = (IIOMetadataNode) item;
IIOMetadataNode found = findNodeRecursive(child, nodeName);
if (found != null) {
return found;
}
}
}
@ -156,43 +194,109 @@ public class FixedPictureUtils {
}
/**
* JDK8JDK8 Graphics2D
* JDK 8
*/
private static BufferedImage rotateImageForJdk8(BufferedImage img, int angle) {
private static int detectImageOrientation(byte[] imageBytes) {
try {
BufferedImage img = readImage(imageBytes);
if (img.getHeight() > img.getWidth() && (double) img.getHeight() / img.getWidth() > 1.2) {
return 90;
}
return 0;
} catch (Exception e) {
return 0;
}
}
/**
* JDK 8
*/
private static BufferedImage readImage(byte[] imageBytes) throws IOException {
try (ByteArrayInputStream bais = new ByteArrayInputStream(imageBytes)) {
return ImageIO.read(bais);
}
}
/**
* JDK 8AffineTransform
*/
private static BufferedImage rotateImage(BufferedImage img, int angle) {
int width = img.getWidth();
int height = img.getHeight();
// 计算旋转后的画布尺寸
int newWidth = (angle % 180 == 0) ? width : height;
int newHeight = (angle % 180 == 0) ? height : width;
// 计算旋转后的尺寸
int newWidth = width;
int newHeight = height;
if (angle == 90 || angle == 270) {
newWidth = height;
newHeight = width;
}
// JDK8兼容创建BufferedImage避免透明通道问题
// JDK 8兼容的AffineTransform使用方式
AffineTransform transform = new AffineTransform();
transform.translate(newWidth / 2.0, newHeight / 2.0);
transform.rotate(Math.toRadians(angle));
transform.translate(-width / 2.0, -height / 2.0);
// 创建旋转后的图片JDK 8兼容的类型
BufferedImage rotated = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = rotated.createGraphics();
// 抗锯齿避免旋转后模糊JDK8必须
// JDK 8支持的渲染参数
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
// 旋转图片
g2d.rotate(Math.toRadians(angle), newWidth / 2.0, newHeight / 2.0);
g2d.drawImage(img, (newWidth - width) / 2, (newHeight - height) / 2, null);
// 绘制旋转后的图片
g2d.drawImage(img, transform, null);
g2d.dispose();
return rotated;
}
/**
* JDK8jpg/png
* JDK 8
*/
private static String getImageFormatForJdk8(byte[] imageBytes) {
if (imageBytes.length < 4) return "jpg";
// JDK8字节判断兼容所有系统
private static String getImageFormat(byte[] imageBytes) {
if (imageBytes.length < 8) {
return "jpg";
}
// JPG
if (imageBytes[0] == (byte) 0xFF && imageBytes[1] == (byte) 0xD8) {
return "jpg";
} else if (imageBytes[0] == (byte)0x89 && imageBytes[1] == (byte)0x50) {
}
// PNG
else if (imageBytes[0] == (byte) 0x89 && imageBytes[1] == (byte) 0x50
&& imageBytes[2] == (byte) 0x4E && imageBytes[3] == (byte) 0x47) {
return "png";
}
// WEBP
else if (imageBytes[0] == (byte) 0x52 && imageBytes[1] == (byte) 0x49
&& imageBytes[2] == (byte) 0x46 && imageBytes[3] == (byte) 0x46) {
return "webp";
}
// BMP
else if (imageBytes[0] == (byte) 0x42 && imageBytes[1] == (byte) 0x4D) {
return "bmp";
}
return "jpg";
}
// -------------------- 应急方法:手动指定旋转角度 --------------------
/**
* JDK 8
*/
public static PictureRenderData ofNormalUrlWithRotate(String imgUrl, int width, int height, int rotateAngle) throws IOException {
byte[] imgBytes = readBytesFromUrl(imgUrl);
BufferedImage original = readImage(imgBytes);
BufferedImage rotated = rotateImage(original, rotateAngle);
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
ImageIO.write(rotated, "jpg", baos);
return Pictures.ofBytes(baos.toByteArray())
.size(width, height)
.create();
}
}
}