|
@@ -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;
|
|
@@ -205,190 +206,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();
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
/**
|
|
|
* 设置图片的定位、大小和合并单元格处理
|
|
|
*
|