修改描述
parent
36f8af6a76
commit
473998f9ce
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,24 +28,21 @@ 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* JDK8原生:从URL读取字节数组(无第三方依赖)
|
||||
* JDK 8原生:从URL读取字节数组(无第三方依赖)
|
||||
*/
|
||||
private static byte[] readBytesFromUrl(String url) throws IOException {
|
||||
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 {
|
|||
}
|
||||
|
||||
/**
|
||||
* 核心:修复图片EXIF旋转(移除containsTree()依赖)
|
||||
* 核心:修复图片旋转(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 8兼容:解析EXIF旋转角度(替换switch表达式)
|
||||
*/
|
||||
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;
|
||||
try {
|
||||
metadataTree = metadata.getAsTree(exifFormat);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// 不支持该格式,返回0度
|
||||
return 0;
|
||||
// 尝试所有支持的EXIF格式
|
||||
String[] formats = {"javax_imageio_jpeg_image_1.0", "javax_imageio_tiff_image_1.0"};
|
||||
for (String format : formats) {
|
||||
try {
|
||||
IIOMetadataNode root = (IIOMetadataNode) metadata.getAsTree(format);
|
||||
rotation = parseOrientationFromMetadata(root);
|
||||
if (rotation != 0) {
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
reader.dispose();
|
||||
return rotation;
|
||||
} catch (Exception e) {
|
||||
// 任何异常都返回0度(比如元数据解析失败)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* JDK8适配:获取XML子节点
|
||||
* JDK 8兼容:解析Orientation(使用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 {
|
|||
}
|
||||
|
||||
/**
|
||||
* JDK8适配:旋转图片(解决JDK8 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 8兼容:旋转图片(简化AffineTransform)
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* JDK8适配:识别图片格式(jpg/png)
|
||||
* JDK 8兼容:识别图片格式
|
||||
*/
|
||||
private static String getImageFormatForJdk8(byte[] imageBytes) {
|
||||
if (imageBytes.length < 4) return "jpg";
|
||||
// JDK8字节判断,兼容所有系统
|
||||
if (imageBytes[0] == (byte)0xFF && imageBytes[1] == (byte)0xD8) {
|
||||
private static String getImageFormat(byte[] imageBytes) {
|
||||
if (imageBytes.length < 8) {
|
||||
return "jpg";
|
||||
} else if (imageBytes[0] == (byte)0x89 && imageBytes[1] == (byte)0x50) {
|
||||
}
|
||||
|
||||
// JPG
|
||||
if (imageBytes[0] == (byte) 0xFF && imageBytes[1] == (byte) 0xD8) {
|
||||
return "jpg";
|
||||
}
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue