From 31e179d5f67393fc081a46e5e0912e1bf9e99b75 Mon Sep 17 00:00:00 2001 From: zhaokai Date: Wed, 11 Mar 2026 10:27:02 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=AF=BC=E5=87=BA=E4=B8=80?= =?UTF-8?q?=E4=BA=BA=E4=B8=80=E6=A1=A3=E4=BA=BA=E8=84=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/archives/ArchivesQueryExe.java | 28 +-- .../edu/domain/utils/FixedPictureUtils.java | 199 ------------------ .../zcloud/edu/domain/utils/ImageUtil.java | 2 +- 3 files changed, 12 insertions(+), 217 deletions(-) delete mode 100644 web-domain/src/main/java/com/zcloud/edu/domain/utils/FixedPictureUtils.java diff --git a/web-app/src/main/java/com/zcloud/edu/command/query/archives/ArchivesQueryExe.java b/web-app/src/main/java/com/zcloud/edu/command/query/archives/ArchivesQueryExe.java index f970615..7d9fb99 100644 --- a/web-app/src/main/java/com/zcloud/edu/command/query/archives/ArchivesQueryExe.java +++ b/web-app/src/main/java/com/zcloud/edu/command/query/archives/ArchivesQueryExe.java @@ -4,8 +4,6 @@ import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.json.JSONUtil; import com.alibaba.cola.dto.PageResponse; -import com.alibaba.cola.exception.BizException; -import com.alibaba.cola.statemachine.impl.SysOutVisitor; import com.deepoove.poi.data.PictureRenderData; import com.deepoove.poi.data.PictureType; import com.deepoove.poi.data.Pictures; @@ -22,7 +20,6 @@ import com.zcloud.edu.domain.model.archives.ArchivesPdfFileE; import com.zcloud.edu.domain.model.archives.ClassArchivesE; import com.zcloud.edu.domain.model.archives.PersonArchivesE; import com.zcloud.edu.domain.model.study.*; -import com.zcloud.edu.domain.utils.FixedPictureUtils; import com.zcloud.edu.domain.utils.ImageUtil; import com.zcloud.edu.dto.archives.ArchivesPdfFilePageQry; import com.zcloud.edu.dto.archives.ArchivesQry; @@ -47,21 +44,16 @@ import com.zcloud.gbscommon.utils.*; import com.zcloud.gbscommon.zcloudimgfiles.facade.ZcloudImgFilesFacade; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; -import net.coobird.thumbnailator.Thumbnails; import org.apache.commons.beanutils.PropertyUtils; import org.apache.commons.io.output.ByteArrayOutputStream; import org.apache.dubbo.config.annotation.DubboReference; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; import org.springframework.util.ObjectUtils; -import java.io.ByteArrayInputStream; import java.io.File; -import java.io.InputStream; -import java.net.URL; import java.nio.file.Files; import java.nio.file.Paths; import java.time.format.DateTimeFormatter; @@ -427,7 +419,8 @@ public class ArchivesQueryExe { public byte[] execteStudentRecord(PersonArchivesDTO params){ try { Map workItem = PropertyUtils.describe( params); - PictureRenderData picture = Pictures.ofUrl(fileUrlConfig.getPrefixUrl() + params.getUserAvatarUrl(), PictureType.JPEG).size(100, 100).create();//网络图片地址 +// PictureRenderData picture = Pictures.ofUrl(fileUrlConfig.getPrefixUrl() + params.getUserAvatarUrl(), PictureType.JPEG).size(100, 100).create();//网络图片地址 + PictureRenderData picture = ImageUtil.createWithThumbnailator(fileUrlConfig.getPrefixUrl() + params.getUserAvatarUrl(), 100, 100); workItem.put("img", picture); String templatePath = "templates/template/studentInfo.docx"; ByteArrayOutputStream outputStream = Tools.renderTemplate(templatePath, workItem); @@ -444,11 +437,13 @@ public class ArchivesQueryExe { try { Map workItem = PropertyUtils.describe( params); if(!ObjectUtils.isEmpty(params.getSignFaceUrl())){ - PictureRenderData signFacePicture = Pictures.ofUrl(fileUrlConfig.getPrefixUrl() + params.getSignFaceUrl(), PictureType.JPEG).size(100, 100).create();//网络图片地址 +// PictureRenderData signFacePicture = Pictures.ofUrl(fileUrlConfig.getPrefixUrl() + params.getSignFaceUrl(), PictureType.JPEG).size(100, 100).create();//网络图片地址 + PictureRenderData signFacePicture = ImageUtil.createWithThumbnailator(fileUrlConfig.getPrefixUrl() + params.getSignFaceUrl(), 100, 100); workItem.put("signFacePicture", signFacePicture); } if (!ObjectUtils.isEmpty(params.getExamSignFaceUrl())) { - PictureRenderData examSignFacePicture = Pictures.ofUrl(fileUrlConfig.getPrefixUrl() + params.getExamSignFaceUrl(), PictureType.JPEG).size(100, 100).create();//网络图片地址 +// PictureRenderData examSignFacePicture = Pictures.ofUrl(fileUrlConfig.getPrefixUrl() + params.getExamSignFaceUrl(), PictureType.JPEG).size(100, 100).create();//网络图片地址 + PictureRenderData examSignFacePicture = ImageUtil.createWithThumbnailator(fileUrlConfig.getPrefixUrl() + params.getExamSignFaceUrl(), 100, 100); workItem.put("examSignFacePicture", examSignFacePicture); // String imageUrl = fileUrlConfig.getPrefixUrl() + params.getExamSignFaceUrl(); // try (InputStream is = new URL(imageUrl).openStream()) { @@ -522,7 +517,8 @@ public class ArchivesQueryExe { workItem.put("name", params.getName()); workItem.put("className", params.getClassName()); if(!ObjectUtils.isEmpty(params.getStudentExamRecord().getSignUrl())){ - PictureRenderData signPicture = Pictures.ofUrl(fileUrlConfig.getPrefixUrl() + params.getStudentExamRecord().getSignUrl(), PictureType.JPEG).size(50, 15).create();//网络图片地址 +// PictureRenderData signPicture = Pictures.ofUrl(fileUrlConfig.getPrefixUrl() + params.getStudentExamRecord().getSignUrl(), PictureType.JPEG).size(50, 15).create();//网络图片地址 + PictureRenderData signPicture = ImageUtil.createWithThumbnailator(fileUrlConfig.getPrefixUrl() + params.getStudentExamRecord(), 50, 15); workItem.put("signPicture", signPicture); } String templatePath = "templates/template/exam.docx"; @@ -879,7 +875,9 @@ public class ArchivesQueryExe { AtomicInteger atomicIndex = new AtomicInteger(1); for (StudentSignCO item : params.getSignList()){ Map itemMap = PropertyUtils.describe(item); - PictureRenderData picture = Pictures.ofUrl(fileUrlConfig.getPrefixUrl() + item.getSignUrl(), PictureType.JPEG).size(100, 100).create();//网络图片地址 +// PictureRenderData picture = Pictures.ofUrl(fileUrlConfig.getPrefixUrl() + item.getSignUrl(), PictureType.JPEG).size(100, 100).create();//网络图片地址 + PictureRenderData picture = ImageUtil.createWithThumbnailator(fileUrlConfig.getPrefixUrl() + item.getSignUrl(), 100, 100); + itemMap.put("picture", picture); int index = atomicIndex.getAndIncrement(); itemMap.put("index", index); @@ -913,21 +911,17 @@ public class ArchivesQueryExe { Map itemMap = PropertyUtils.describe(item); if(!ObjectUtils.isEmpty(item.getSignFaceUrl())){ // PictureRenderData signPicture = Pictures.ofUrl(fileUrlConfig.getPrefixUrl() + item.getSignFaceUrl(), PictureType.JPEG).size(50, 50).create();//网络图片地址 -// PictureRenderData signPicture = FixedPictureUtils.ofNormalUrl(fileUrlConfig.getPrefixUrl() + item.getSignFaceUrl(), 50, 50); PictureRenderData signPicture = ImageUtil.createWithThumbnailator(fileUrlConfig.getPrefixUrl() + item.getSignFaceUrl(), 50, 50); - itemMap.put("signPicture", signPicture); } if(!ObjectUtils.isEmpty(item.getExamFaceUrl())){ // PictureRenderData examPicture = Pictures.ofUrl(fileUrlConfig.getPrefixUrl() + item.getExamFaceUrl(), PictureType.JPEG).size(50, 50).create();//网络图片地址 -// PictureRenderData examPicture = FixedPictureUtils.ofNormalUrl(fileUrlConfig.getPrefixUrl() + item.getExamFaceUrl(), 50, 50); PictureRenderData examPicture = ImageUtil.createWithThumbnailator(fileUrlConfig.getPrefixUrl() + item.getExamFaceUrl(), 50, 50); itemMap.put("examPicture", examPicture); } if(!ObjectUtils.isEmpty(item.getUserAvatarUrl())){ // PictureRenderData facePicture = Pictures.ofUrl(fileUrlConfig.getPrefixUrl() + item.getUserAvatarUrl(), PictureType.JPEG).size(50, 50).style("rotation:90;").create();//网络图片地址 -// PictureRenderData facePicture = FixedPictureUtils.ofNormalUrl(fileUrlConfig.getPrefixUrl() + item.getUserAvatarUrl(), 50, 50); PictureRenderData facePicture = ImageUtil.createWithThumbnailator(fileUrlConfig.getPrefixUrl() + item.getUserAvatarUrl(), 50, 50); itemMap.put("facePicture", facePicture); diff --git a/web-domain/src/main/java/com/zcloud/edu/domain/utils/FixedPictureUtils.java b/web-domain/src/main/java/com/zcloud/edu/domain/utils/FixedPictureUtils.java deleted file mode 100644 index fce37d2..0000000 --- a/web-domain/src/main/java/com/zcloud/edu/domain/utils/FixedPictureUtils.java +++ /dev/null @@ -1,199 +0,0 @@ -package com.zcloud.edu.domain.utils; - -import com.deepoove.poi.data.PictureRenderData; -import com.deepoove.poi.data.Pictures; - - -import javax.imageio.ImageIO; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.Iterator; -import javax.imageio.ImageReader; -import javax.imageio.metadata.IIOMetadata; -import javax.imageio.metadata.IIOMetadataNode; -import javax.imageio.stream.ImageInputStream; - -public class FixedPictureUtils { - /** - * 替代Pictures.ofUrl(),返回正常方向的PictureRenderData - * @param imgUrl 图片URL - * @param width 插入宽度(px) - * @param height 插入高度(px) - * @return 正常显示的PictureRenderData - * @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方法) - return Pictures.ofBytes(fixedImgBytes) - .size(width, height) - .create(); - } - - /** - * JDK8原生:从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]; - int len; - while ((len = is.read(buffer)) != -1) { - baos.write(buffer, 0, len); - } - return baos.toByteArray(); - } - } - - /** - * 核心:修复图片EXIF旋转(移除containsTree()依赖) - */ - private static byte[] fixImageRotationForJdk8(byte[] imageBytes) throws IOException { - try (ByteArrayInputStream bais = new ByteArrayInputStream(imageBytes); - ImageInputStream iis = ImageIO.createImageInputStream(bais)) { - - // 获取图片读取器 - Iterator readers = ImageIO.getImageReaders(iis); - if (!readers.hasNext()) { - return imageBytes; // 无读取器,返回原字节 - } - - 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"; - - // 直接尝试获取元数据树,不先判断containsTree() - // 捕获异常即可,无需提前检查 - Object metadataTree; - 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; - } - } catch (Exception e) { - // 任何异常都返回0度(比如元数据解析失败) - return 0; - } - } - - /** - * JDK8适配:获取XML子节点 - */ - private static IIOMetadataNode getChildNodeForJdk8(IIOMetadataNode parent, String name) { - 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; - } - } - } - return null; - } - - /** - * JDK8适配:旋转图片(解决JDK8 Graphics2D兼容问题) - */ - private static BufferedImage rotateImageForJdk8(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; - - // JDK8兼容:创建BufferedImage(避免透明通道问题) - BufferedImage rotated = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB); - Graphics2D g2d = rotated.createGraphics(); - - // 抗锯齿,避免旋转后模糊(JDK8必须) - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); - - // 旋转图片 - g2d.rotate(Math.toRadians(angle), newWidth / 2.0, newHeight / 2.0); - g2d.drawImage(img, (newWidth - width) / 2, (newHeight - height) / 2, null); - g2d.dispose(); - - return rotated; - } - - /** - * JDK8适配:识别图片格式(jpg/png) - */ - private static String getImageFormatForJdk8(byte[] imageBytes) { - if (imageBytes.length < 4) return "jpg"; - // JDK8字节判断,兼容所有系统 - if (imageBytes[0] == (byte)0xFF && imageBytes[1] == (byte)0xD8) { - return "jpg"; - } else if (imageBytes[0] == (byte)0x89 && imageBytes[1] == (byte)0x50) { - return "png"; - } - return "jpg"; - } -} \ No newline at end of file diff --git a/web-domain/src/main/java/com/zcloud/edu/domain/utils/ImageUtil.java b/web-domain/src/main/java/com/zcloud/edu/domain/utils/ImageUtil.java index 25b96d7..763bcb9 100644 --- a/web-domain/src/main/java/com/zcloud/edu/domain/utils/ImageUtil.java +++ b/web-domain/src/main/java/com/zcloud/edu/domain/utils/ImageUtil.java @@ -21,7 +21,7 @@ public class ImageUtil { // 使用Thumbnailator处理 - 一行代码搞定旋转和缩放 BufferedImage processed = Thumbnails.of(original) .size(width, height) - .rotate(180) // 如果需要固定旋转,可以直接指定 + .rotate(90) // 如果需要固定旋转,可以直接指定 .asBufferedImage(); // 或者使用EXIF自动旋转(如果Thumbnailator版本支持)