Prechádzať zdrojové kódy

影像资料上传图片写入失败

cr 5 dní pred
rodič
commit
b4f5843524

+ 218 - 165
blade-service/blade-business/src/main/java/org/springblade/business/utils/FileUtils.java

@@ -8,6 +8,7 @@ import com.drew.metadata.exif.ExifIFD0Directory;
 import com.itextpdf.text.Document;
 import com.itextpdf.text.pdf.PdfCopy;
 import com.itextpdf.text.pdf.PdfReader;
+import net.coobird.thumbnailator.Thumbnails;
 import org.apache.commons.lang.StringUtils;
 import org.apache.poi.ss.usermodel.ClientAnchor;
 import org.apache.poi.ss.usermodel.Sheet;
@@ -208,190 +209,242 @@ public class FileUtils {
 //        return baos.toByteArray();
 //    }
 
-    /**
-     // * 图片缩放、压缩、旋转处理
-     // *
-     // * @param imageData
-     // * @return
-     // * @throws IOException
-     // * @throws ImageProcessingException
-     // * @throws MetadataException
-     //     */
     public static byte[] compressImage(byte[] imageData) throws IOException {
-        if (imageData == null || imageData.length == 0) {
-            throw new IllegalArgumentException("图像数据不能为空");
-        }
-
+        // 读取元数据判断是否需要旋转
+        int orientation = 1;
         try {
-            // 读取原始图像(处理旋转问题)
             Metadata metadata = ImageMetadataReader.readMetadata(new ByteArrayInputStream(imageData));
-            if (metadata.containsDirectoryOfType(ExifIFD0Directory.class)) {
-                ExifIFD0Directory exifIFD0Directory = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
-                if (exifIFD0Directory.containsTag(ExifIFD0Directory.TAG_ORIENTATION)) {
-                    // 获取 Orientation 标签的值
-                    int orientation = exifIFD0Directory.getInt(ExifIFD0Directory.TAG_ORIENTATION);
-                    // 需要旋转图片
-                    BufferedImage originalImage = ImageIO.read(new ByteArrayInputStream(imageData));
-                    if (originalImage == null) {
-                        throw new IOException("无法读取图像数据");
-                    }
-
-                    AffineTransform transform = new AffineTransform();
-                    if (orientation == 6) {
-                        transform.rotate(Math.PI / 2, originalImage.getWidth() / 2, originalImage.getHeight() / 2);
-                    } else if (orientation == 8) {
-                        transform.rotate(-Math.PI / 2, originalImage.getWidth() / 2, originalImage.getHeight() / 2);
-                    }
-                    AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR);
-                    originalImage = op.filter(originalImage, null);
-                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
-                    ImageIO.write(originalImage, "jpg", baos);
-                    imageData = baos.toByteArray();
-                }
+            ExifIFD0Directory exifIFD0Directory = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
+            if (exifIFD0Directory != null && exifIFD0Directory.containsTag(ExifIFD0Directory.TAG_ORIENTATION)) {
+                orientation = exifIFD0Directory.getInt(ExifIFD0Directory.TAG_ORIENTATION);
             }
         } catch (Exception e) {
-            // 如果EXIF处理失败,继续处理原始图像数据
-            System.err.println("处理图像EXIF信息时出错: " + e.getMessage());
-        }
-
-        // 缩放图像
-        String formatName = "JPEG";
-        ByteArrayInputStream bais = new ByteArrayInputStream(imageData);
-        BufferedImage originalImage = ImageIO.read(bais);
-
-        if (originalImage == null) {
-            throw new IOException("无法读取图像数据");
+            // 元数据读取失败不影响主要流程
         }
 
-        // 确保图像是兼容的格式
-        BufferedImage compatibleImage = convertToSupportedFormat(originalImage);
-
-        long sizeLimit = 366912; //358KB
-        int width = 768;
-        int height = 1024;
-
-        // 使用更高质量的缩放方法
-        Image scaledImage = originalImage.getScaledInstance(width, height, Image.SCALE_SMOOTH);
-        BufferedImage resizedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
-        Graphics2D g2d = resizedImage.createGraphics();
-        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
-        g2d.drawImage(scaledImage, 0, 0, null);
-        g2d.dispose();
+        // 使用Thumbnails处理旋转和压缩
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        long sizeLimit = 366912; // 358KB
 
-        // 压缩图像
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
         try {
-            boolean success = ImageIO.write(resizedImage, formatName, baos);
-            if (!success) {
-                throw new IOException("无法写入JPEG格式图像");
-            }
-        } catch (Exception e) {
-            // 尝试使用ImageWriter直接写入
-            writeImageWithDefaultSettings(resizedImage, baos, formatName);
-        }
-
-        if (baos.size() <= sizeLimit) {
-            // 图片大小已经小于等于目标大小,直接返回原始数据
-            return baos.toByteArray();
-        }
-
-        float quality = 0.9f; // 初始化压缩质量
-        int retries = 10; // 最多尝试 10 次
-
-        while (baos.size() > sizeLimit && retries > 0) {
-            // 压缩图像并重新计算压缩质量
-            byte[] data = baos.toByteArray();
-            bais = new ByteArrayInputStream(data);
-            BufferedImage compressedImage = ImageIO.read(bais);
-
-            if (compressedImage == null) {
-                throw new IOException("无法读取压缩后的图像数据");
+            Thumbnails.of(new ByteArrayInputStream(imageData))
+                    .size(768, 1024)
+                    .outputFormat("jpg")
+                    .outputQuality(0.9)
+                    .useExifOrientation(true) // 自动处理EXIF旋转信息
+                    .toOutputStream(outputStream);
+
+            // 如果大小已满足要求,直接返回
+            if (outputStream.size() <= sizeLimit) {
+                return outputStream.toByteArray();
             }
 
-            baos.reset();
-
-            ImageWriter writer = null;
-            Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(formatName.toLowerCase());
-            if (!writers.hasNext()) {
-                // 尝试其他格式
-                writers = ImageIO.getImageWritersByFormatName("jpeg");
-            }
-
-            if (writers.hasNext()) {
-                writer = writers.next();
-            } else {
-                throw new IllegalArgumentException("Unsupported image format: " + formatName);
-            }
+            // 逐步降低质量直到满足大小要求
+            double quality = 0.8;
+            int attempts = 5;
 
-            try {
-                ImageWriteParam writeParam = writer.getDefaultWriteParam();
-                if (writeParam.canWriteCompressed()) {
-                    writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
-                    writeParam.setCompressionQuality(quality);
-                }
+            while (outputStream.size() > sizeLimit && attempts-- > 0) {
+                outputStream.reset();
+                Thumbnails.of(new ByteArrayInputStream(imageData))
+                        .size(768, 1024)
+                        .outputFormat("jpg")
+                        .outputQuality(quality)
+                        .useExifOrientation(true)
+                        .toOutputStream(outputStream);
 
-                try (ImageOutputStream ios = ImageIO.createImageOutputStream(baos)) {
-                    writer.setOutput(ios);
-                    writer.write(null, new IIOImage(compressedImage, null, null), writeParam);
-                }
-            } finally {
-                writer.dispose();
+                quality *= 0.8; // 每次降低20%质量
             }
 
-            float ratio = sizeLimit * 1.0f / baos.size();
-            quality *= Math.sqrt(ratio);
-            retries--;
-        }
-
-        return baos.toByteArray();
-    }
-
-    /**
-     * 转换图像为支持的格式
-     */
-    private static BufferedImage convertToSupportedFormat(BufferedImage image) {
-        if (image == null) return null;
-
-        // 检查是否为支持的类型
-        int type = image.getType();
-        if (type != BufferedImage.TYPE_INT_RGB && type != BufferedImage.TYPE_INT_ARGB) {
-            // 转换为支持的格式
-            BufferedImage converted = new BufferedImage(
-                    image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
-            Graphics2D g = converted.createGraphics();
-            g.setColor(Color.WHITE);
-            g.fillRect(0, 0, converted.getWidth(), converted.getHeight());
-            g.drawImage(image, 0, 0, null);
-            g.dispose();
-            return converted;
-        }
-        return image;
-    }
-
-    /**
-     * 使用默认设置写入图像
-     */
-    private static void writeImageWithDefaultSettings(BufferedImage image, ByteArrayOutputStream baos, String formatName) throws IOException {
-        Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(formatName.toLowerCase());
-        if (!writers.hasNext()) {
-            writers = ImageIO.getImageWritersByFormatName("jpeg");
-        }
-
-        if (!writers.hasNext()) {
-            throw new IOException("找不到JPEG图像写入器");
-        }
-
-        ImageWriter writer = writers.next();
-        ImageWriteParam param = writer.getDefaultWriteParam();
-
-        try (ImageOutputStream ios = ImageIO.createImageOutputStream(baos)) {
-            writer.setOutput(ios);
-            writer.write(null, new IIOImage(image, null, null), param);
+            return outputStream.toByteArray();
         } finally {
-            writer.dispose();
+            outputStream.close();
         }
     }
 
+//    /**
+//     // * 图片缩放、压缩、旋转处理
+//     // *
+//     // * @param imageData
+//     // * @return
+//     // * @throws IOException
+//     // * @throws ImageProcessingException
+//     // * @throws MetadataException
+//     //     */
+//    public static byte[] compressImage(byte[] imageData) throws IOException {
+//        if (imageData == null || imageData.length == 0) {
+//            throw new IllegalArgumentException("图像数据不能为空");
+//        }
+//
+//        try {
+//            // 读取原始图像(处理旋转问题)
+//            Metadata metadata = ImageMetadataReader.readMetadata(new ByteArrayInputStream(imageData));
+//            if (metadata.containsDirectoryOfType(ExifIFD0Directory.class)) {
+//                ExifIFD0Directory exifIFD0Directory = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
+//                if (exifIFD0Directory.containsTag(ExifIFD0Directory.TAG_ORIENTATION)) {
+//                    // 获取 Orientation 标签的值
+//                    int orientation = exifIFD0Directory.getInt(ExifIFD0Directory.TAG_ORIENTATION);
+//                    // 需要旋转图片
+//                    BufferedImage originalImage = ImageIO.read(new ByteArrayInputStream(imageData));
+//                    if (originalImage == null) {
+//                        throw new IOException("无法读取图像数据");
+//                    }
+//
+//                    AffineTransform transform = new AffineTransform();
+//                    if (orientation == 6) {
+//                        transform.rotate(Math.PI / 2, originalImage.getWidth() / 2, originalImage.getHeight() / 2);
+//                    } else if (orientation == 8) {
+//                        transform.rotate(-Math.PI / 2, originalImage.getWidth() / 2, originalImage.getHeight() / 2);
+//                    }
+//                    AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR);
+//                    originalImage = op.filter(originalImage, null);
+//                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+//                    ImageIO.write(originalImage, "jpg", baos);
+//                    imageData = baos.toByteArray();
+//                }
+//            }
+//        } catch (Exception e) {
+//            // 如果EXIF处理失败,继续处理原始图像数据
+//            System.err.println("处理图像EXIF信息时出错: " + e.getMessage());
+//        }
+//
+//        // 缩放图像
+//        String formatName = "JPEG";
+//        ByteArrayInputStream bais = new ByteArrayInputStream(imageData);
+//        BufferedImage originalImage = ImageIO.read(bais);
+//
+//        if (originalImage == null) {
+//            throw new IOException("无法读取图像数据");
+//        }
+//
+//        // 确保图像是兼容的格式
+//        BufferedImage compatibleImage = convertToSupportedFormat(originalImage);
+//
+//        long sizeLimit = 366912; //358KB
+//        int width = 768;
+//        int height = 1024;
+//
+//        // 使用更高质量的缩放方法
+//        Image scaledImage = originalImage.getScaledInstance(width, height, Image.SCALE_SMOOTH);
+//        BufferedImage resizedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+//        Graphics2D g2d = resizedImage.createGraphics();
+//        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+//        g2d.drawImage(scaledImage, 0, 0, null);
+//        g2d.dispose();
+//
+//        // 压缩图像
+//        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+//        try {
+//            boolean success = ImageIO.write(resizedImage, formatName, baos);
+//            if (!success) {
+//                throw new IOException("无法写入JPEG格式图像");
+//            }
+//        } catch (Exception e) {
+//            // 尝试使用ImageWriter直接写入
+//            writeImageWithDefaultSettings(resizedImage, baos, formatName);
+//        }
+//
+//        if (baos.size() <= sizeLimit) {
+//            // 图片大小已经小于等于目标大小,直接返回原始数据
+//            return baos.toByteArray();
+//        }
+//
+//        float quality = 0.9f; // 初始化压缩质量
+//        int retries = 10; // 最多尝试 10 次
+//
+//        while (baos.size() > sizeLimit && retries > 0) {
+//            // 压缩图像并重新计算压缩质量
+//            byte[] data = baos.toByteArray();
+//            bais = new ByteArrayInputStream(data);
+//            BufferedImage compressedImage = ImageIO.read(bais);
+//
+//            if (compressedImage == null) {
+//                throw new IOException("无法读取压缩后的图像数据");
+//            }
+//
+//            baos.reset();
+//
+//            ImageWriter writer = null;
+//            Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(formatName.toLowerCase());
+//            if (!writers.hasNext()) {
+//                // 尝试其他格式
+//                writers = ImageIO.getImageWritersByFormatName("jpeg");
+//            }
+//
+//            if (writers.hasNext()) {
+//                writer = writers.next();
+//            } else {
+//                throw new IllegalArgumentException("Unsupported image format: " + formatName);
+//            }
+//
+//            try {
+//                ImageWriteParam writeParam = writer.getDefaultWriteParam();
+//                if (writeParam.canWriteCompressed()) {
+//                    writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
+//                    writeParam.setCompressionQuality(quality);
+//                }
+//
+//                try (ImageOutputStream ios = ImageIO.createImageOutputStream(baos)) {
+//                    writer.setOutput(ios);
+//                    writer.write(null, new IIOImage(compressedImage, null, null), writeParam);
+//                }
+//            } finally {
+//                writer.dispose();
+//            }
+//
+//            float ratio = sizeLimit * 1.0f / baos.size();
+//            quality *= Math.sqrt(ratio);
+//            retries--;
+//        }
+//
+//        return baos.toByteArray();
+//    }
+//
+//    /**
+//     * 转换图像为支持的格式
+//     */
+//    private static BufferedImage convertToSupportedFormat(BufferedImage image) {
+//        if (image == null) return null;
+//
+//        // 检查是否为支持的类型
+//        int type = image.getType();
+//        if (type != BufferedImage.TYPE_INT_RGB && type != BufferedImage.TYPE_INT_ARGB) {
+//            // 转换为支持的格式
+//            BufferedImage converted = new BufferedImage(
+//                    image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
+//            Graphics2D g = converted.createGraphics();
+//            g.setColor(Color.WHITE);
+//            g.fillRect(0, 0, converted.getWidth(), converted.getHeight());
+//            g.drawImage(image, 0, 0, null);
+//            g.dispose();
+//            return converted;
+//        }
+//        return image;
+//    }
+//
+//    /**
+//     * 使用默认设置写入图像
+//     */
+//    private static void writeImageWithDefaultSettings(BufferedImage image, ByteArrayOutputStream baos, String formatName) throws IOException {
+//        Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(formatName.toLowerCase());
+//        if (!writers.hasNext()) {
+//            writers = ImageIO.getImageWritersByFormatName("jpeg");
+//        }
+//
+//        if (!writers.hasNext()) {
+//            throw new IOException("找不到JPEG图像写入器");
+//        }
+//
+//        ImageWriter writer = writers.next();
+//        ImageWriteParam param = writer.getDefaultWriteParam();
+//
+//        try (ImageOutputStream ios = ImageIO.createImageOutputStream(baos)) {
+//            writer.setOutput(ios);
+//            writer.write(null, new IIOImage(image, null, null), param);
+//        } finally {
+//            writer.dispose();
+//        }
+//    }
+
     /**
      * 设置图片的定位、大小和合并单元格处理
      *