吉安感知网项目-后端
xiebin
2026-01-06 d207a86cdf1ab52ef8cb7cd83bad8fceab8038cf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
package org.sxkj.common.utils;
 
 
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import javax.imageio.stream.MemoryCacheImageOutputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Iterator;
 
public class ImgZipUtil {
 
    /**
     * tif 压缩转换成 jpeg
     * @param inputFile
     * @param targetSizeKB 压缩目标大小
     * @param targetName 压缩目标名称,例如 _small,_show
     * @return
     */
    public static File compressTifToJpeg(File inputFile,int targetSizeKB,String targetName) throws IOException {
        File jpeg = TifCompressorUtils.convertTifToJpeg(inputFile, ".jpeg");
        File file = compressImage(jpeg, targetSizeKB, targetName);
        return file;
    }
 
    /**
     * 图片压缩
     * @param inputFile
     * @param targetSizeKB 压缩目标大小
     * @param targetName 压缩目标名称,例如 _small,_show
     * @return
     * @throws IOException
     */
    public static File compressImage(File inputFile, int targetSizeKB,String targetName) throws IOException {
        BufferedImage image = ImageIO.read(inputFile);
 
        // 计算目标图片的尺寸
        long targetSizeBytes = targetSizeKB * 1024L;
        long originalSizeBytes = getImageSize(image);
        double compressionRatio = (double) targetSizeBytes / originalSizeBytes;
        int targetWidth = (int) (image.getWidth() * Math.sqrt(compressionRatio));
        int targetHeight = (int) (image.getHeight() * Math.sqrt(compressionRatio));
 
        // 使用ImageIO进行压缩
        BufferedImage compressedImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
        Graphics2D graphics = compressedImage.createGraphics();
        graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        graphics.drawImage(image, 0, 0, targetWidth, targetHeight, null);
        graphics.dispose();
 
        String name = inputFile.getName();
        String fileName = name.substring(0,name.lastIndexOf(".")); // 获取文件名称
        String extension = name.substring(name.lastIndexOf(".")); // 获取后缀名
 
        // 将压缩后的图片写入输出文件
        File outputFile = new File(inputFile.getParent(), fileName + targetName + extension);
        ImageIO.write(compressedImage, "jpeg", outputFile);
        return outputFile;
    }
    /**
     * 图片压缩
     *
     * @return
     * @throws IOException
     */
    public static File compressImage(File inputFile, int targetSizeKB) throws IOException {
        BufferedImage image = ImageIO.read(inputFile);
 
        // 计算目标图片的尺寸
        long targetSizeBytes = targetSizeKB * 1024L;
        long originalSizeBytes = getImageSize(image);
        double compressionRatio = (double) targetSizeBytes / originalSizeBytes;
        int targetWidth = (int) (image.getWidth() * Math.sqrt(compressionRatio));
        int targetHeight = (int) (image.getHeight() * Math.sqrt(compressionRatio));
 
        // 使用ImageIO进行压缩
        BufferedImage compressedImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
        Graphics2D graphics = compressedImage.createGraphics();
        graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        graphics.drawImage(image, 0, 0, targetWidth, targetHeight, null);
        graphics.dispose();
 
        // 将压缩后的图片写入输出文件
        File outputFile = new File(inputFile.getParent(), "compressed_" + inputFile.getName());
        ImageIO.write(compressedImage, "jpeg", outputFile);
        return outputFile;
    }
    public static ByteArrayInputStream compressImage(InputStream inputStream, int targetSizeKB) throws IOException {
        if (!inputStream.markSupported()) {
            inputStream = new BufferedInputStream(inputStream);
        }
 
        String format = getOriginalFormat(inputStream); // format will be "jpg" or "png"
 
        BufferedImage image = ImageIO.read(inputStream);
        if (image == null) {
            throw new IOException("图片读取异常");
        }
 
        // 将 "jpeg" 统一为 "jpg" 以便内部处理,虽然 ImageIO 通常两者都认
        String internalFormat = "jpeg".equalsIgnoreCase(format) ? "jpg" : format.toLowerCase();
        long originalSizeBytes = getImageSize(image, internalFormat);
 
        // 如果小于2MB,直接返回原图 (重新编码成原始格式)
//        if (originalSizeBytes < 2 * 1024 * 1024) {
//            ByteArrayOutputStream baos = new ByteArrayOutputStream();
//            if (!ImageIO.write(image, internalFormat, baos)) {
//                throw new IOException("Failed to write original image to byte array for format: " + internalFormat);
//            }
//            return new ByteArrayInputStream(baos.toByteArray());
//        }
 
        long targetSizeBytes = targetSizeKB * 1024L;
        if (targetSizeBytes <= 0) {
            targetSizeBytes = 1;
        }
        if (originalSizeBytes <= 0) {
            originalSizeBytes = 1;
        }
 
        double compressionRatio = (double) targetSizeBytes / originalSizeBytes;
        // 限制压缩比,避免图片缩得太小或反而变大
        compressionRatio = Math.min(1.0, Math.max(0.01, compressionRatio)); // 最小缩小到1%
 
        int targetWidth = (int) (image.getWidth() * Math.sqrt(compressionRatio));
        int targetHeight = (int) (image.getHeight() * Math.sqrt(compressionRatio));
 
        targetWidth = Math.max(1, targetWidth);
        targetHeight = Math.max(1, targetHeight);
 
        BufferedImage compressedImage;
        if ("png".equalsIgnoreCase(internalFormat)) {
            // 对于PNG,保留Alpha通道
            compressedImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_ARGB);
        } else { // "jpg"
            // 对于JPG,使用RGB (JPG不支持Alpha)
            // 如果原始图像有Alpha,转换到RGB时Alpha会丢失
            // 如果原始PNG image.getType() 是 TYPE_CUSTOM 或其他复杂类型,直接绘制到TYPE_INT_RGB可能需要注意
            // 但通常 ImageIO.read() 会返回一个标准类型
            if (image.getType() == BufferedImage.TYPE_INT_ARGB || image.getType() == BufferedImage.TYPE_INT_ARGB_PRE || image.getTransparency() != BufferedImage.OPAQUE) {
                // 如果原图是PNG且有透明度,但目标是JPG,创建一个RGB图像,绘制时透明部分会变黑(或Graphics2D的默认背景色)
                // 更好的做法是先在白色背景上绘制原图,再缩放
                BufferedImage tempImageWithWhiteBg = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
                Graphics2D g2dTemp = tempImageWithWhiteBg.createGraphics();
                g2dTemp.setColor(java.awt.Color.WHITE); // 设置背景为白色
                g2dTemp.fillRect(0, 0, image.getWidth(), image.getHeight());
                g2dTemp.drawImage(image, 0, 0, null);
                g2dTemp.dispose();
                image = tempImageWithWhiteBg; // 后续缩放基于这个去除了透明度的图
            }
            compressedImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
        }
 
        Graphics2D graphics = compressedImage.createGraphics();
        graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        // 如果需要更高质量的缩放(但更慢)
        // graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        // graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        graphics.drawImage(image, 0, 0, targetWidth, targetHeight, null);
        graphics.dispose();
 
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        // 对于JPG,可以尝试设置压缩质量,但这会使大小更难预测
        // if ("jpg".equalsIgnoreCase(internalFormat)) {
        //     Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpeg"); // "jpeg" or "jpg"
        //     if (writers.hasNext()) {
        //         ImageWriter writer = writers.next();
        //         ImageWriteParam param = writer.getDefaultWriteParam();
        //         if (param.canWriteCompressed()) {
        //             param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
        //             param.setCompressionQuality(0.75f); // 0.0 (高压缩) to 1.0 (高质量)
        //         }
        //         ImageOutputStream ios = ImageIO.createImageOutputStream(byteArrayOutputStream);
        //         writer.setOutput(ios);
        //         writer.write(null, new IIOImage(compressedImage, null, null), param);
        //         ios.close();
        //         writer.dispose();
        //     } else {
        //         throw new IOException("No JPEG writer found for setting quality.");
        //     }
        // } else {
        if (!ImageIO.write(compressedImage, internalFormat, byteArrayOutputStream)) {
            throw new IOException("处理缩略图片失败: " + internalFormat);
        }
        // }
 
        return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
    }
 
 
    // 获取图像的字节大小
 
    /**
     * 图片压缩
     *
     * @return
     * @throws IOException
     */
    public static ByteArrayInputStream compressImageByInputString(InputStream inputStream, int targetSizeKB) throws IOException {
        BufferedImage image = ImageIO.read(inputStream);
        // 计算目标图片的尺寸
        long targetSizeBytes = targetSizeKB * 1024L;
        long originalSizeBytes = getImageSize(image);
        double compressionRatio = (double) targetSizeBytes / originalSizeBytes;
        int targetWidth = (int) (image.getWidth() * Math.sqrt(compressionRatio));
        int targetHeight = (int) (image.getHeight() * Math.sqrt(compressionRatio));
        // 使用ImageIO进行压缩
        BufferedImage compressedImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
        Graphics2D graphics = compressedImage.createGraphics();
        graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        graphics.drawImage(image, 0, 0, targetWidth, targetHeight, null);
        // 释放图形上下文
        graphics.dispose();
        // 将处理后的图片转换为输入流
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        // 将压缩后的图片写入输出文件
        ImageIO.write(compressedImage, "jpeg",byteArrayOutputStream);
        // 返回
        return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
    }
 
    public static long getImageSize(BufferedImage image) {
        File tempFile;
        try {
            tempFile = File.createTempFile("temp", ".tmp");
            ImageIO.write(image, "jpg", tempFile);
            long size = tempFile.length();
            tempFile.delete();
            return size;
        } catch (IOException ex) {
            ex.printStackTrace();
            return 0;
        }
    }
    public static long getImageSize(BufferedImage image, String format) throws IOException {
        // 规范化格式名称 (例如,将 "jpeg" 转为 "jpg",全部转为小写)
        String actualFormat = "jpeg".equalsIgnoreCase(format) ? "jpg" : format.toLowerCase();
 
        File tempFile = null;
        try {
            // 1. 创建一个临时文件来写入图像数据
            tempFile = File.createTempFile("temp_img_size_", "." + actualFormat);
            // System.out.println("DEBUG (getImageSize): Temp file will be created at: " + tempFile.getAbsolutePath());
 
            // 2. 获取指定格式的 ImageWriter
            Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(actualFormat);
            if (!writers.hasNext()) {
                // 如果没有找到 ImageWriter,则无法进行写入操作
                throw new IOException("无法找到合适的 ImageWriter 来写入 " + actualFormat + " 格式");
            }
            ImageWriter writer = writers.next(); // 通常使用第一个找到的 writer
 
            // 3. 创建一个 ImageOutputStream 连接到临时文件
            ImageOutputStream ios = null;
            try {
                ios = ImageIO.createImageOutputStream(tempFile);
                if (ios == null) {
                    // 这种情况不常见,但以防万一
                    throw new IOException("无法为临时文件创建 ImageOutputStream: " + tempFile.getAbsolutePath());
                }
 
                // 4. 将 ImageWriter 的输出目标设置为 ImageOutputStream
                writer.setOutput(ios);
 
                // 5. 写入图像数据
                // 使用 IIOImage 包装 BufferedImage,可以传递缩略图和元数据(这里都设为 null)
                // 第三个参数是 ImageWriteParam,设为 null 表示使用默认写入参数
                writer.write(null, new IIOImage(image, null, null), null);
 
                // 6. 确保所有缓冲数据都已写入流 (可选,通常 close() 会做这个)
                // ios.flush();
                // System.out.println("DEBUG (getImageSize): Image written to temp file.");
 
            } finally {
                // 7. 关闭 ImageOutputStream (非常重要,确保数据落盘并释放资源)
                if (ios != null) {
                    try {
                        ios.close();
                        // System.out.println("DEBUG (getImageSize): ImageOutputStream closed.");
                    } catch (IOException e) {
                        // 记录关闭流时的错误,但可能仍需尝试删除文件和释放writer
                        System.err.println("Error closing ImageOutputStream: " + e.getMessage());
                    }
                }
                // 8. 释放 ImageWriter 资源
                if (writer != null) {
                    writer.dispose();
                }
            }
 
            // 9. 获取临时文件的大小
            long length = tempFile.length();
            // System.out.println("DEBUG (getImageSize): Temp file length: " + length + " bytes for format " + actualFormat);
 
            return length;
 
        } finally {
            // 10. 无论成功与否,都尝试删除临时文件
            if (tempFile != null && tempFile.exists()) {
                // System.out.println("DEBUG (getImageSize): Deleting temp file: " + tempFile.getAbsolutePath());
                if (!tempFile.delete()) {
                    System.err.println("警告: 无法删除临时文件: " + tempFile.getAbsolutePath());
                    // 可以选择让它在JVM退出时删除
                    // tempFile.deleteOnExit();
                }
            }
        }
    }
 
    public static String getOriginalFormat(InputStream inputStream) throws IOException {
        if (!inputStream.markSupported()) {
            inputStream = new BufferedInputStream(inputStream);
        }
        inputStream.mark(8); // 8 bytes should be enough for JPG and PNG
        byte[] header = new byte[8];
        int bytesRead = inputStream.read(header);
        inputStream.reset();
 
        if (bytesRead < 4) {
            System.err.println("Warning: Could not read enough bytes to determine format. Defaulting to jpg.");
            return "jpg";
        }
 
        // JPEG: FF D8 FF
        if (header[0] == (byte) 0xFF && header[1] == (byte) 0xD8 && header[2] == (byte) 0xFF) {
            return "jpg"; // 标准的 ImageIO format name 是 "jpeg" 或 "jpg"
        }
        // PNG: 89 50 4E 47 (‰PNG)
        else if (header[0] == (byte) 0x89 && header[1] == 'P' && header[2] == 'N' && header[3] == 'G') {
            return "png";
        }
 
        System.err.println("Warning: Unknown image format based on header (only JPG/PNG supported). Defaulting to jpg.");
        return "jpg"; // 默认 fallback
    }
 
 
    /**
     * 图片压缩
     *
     * @param originalImageFile
     * @param compressionQuality 图片压缩质量 0-1
     * @return
     * @throws IOException
     */
    public static File compressImageAndGetFile(File originalImageFile, float compressionQuality) throws IOException {
        BufferedImage originalImage = ImageIO.read(originalImageFile);
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            ImageWriter writer = ImageIO.getImageWritersByFormatName("jpg").next();
            try (ImageOutputStream ios = new MemoryCacheImageOutputStream(baos)) {
                writer.setOutput(ios);
                ImageWriteParam param = writer.getDefaultWriteParam();
                param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
                param.setCompressionQuality(compressionQuality);
                writer.write(null, new javax.imageio.IIOImage(originalImage, null, null), param);
            } finally {
                writer.dispose();
            }
            InputStream in = new ByteArrayInputStream(baos.toByteArray());
            File tempFile = File.createTempFile("compressed-", ".jpg");
            tempFile.deleteOnExit();
            Files.copy(in, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
            return tempFile;
        }
    }
 
    /**
     * 图片压缩
     *
     * @param inputStream
     * @param compressionQuality 图片压缩质量 0-1
     * @return
     * @throws IOException
     */
    public static ByteArrayInputStream compressImageAndGetByteInputStream(InputStream inputStream, float compressionQuality) throws IOException {
        BufferedImage originalImage = ImageIO.read(inputStream);
        ByteArrayInputStream byteArrayInputStream = null;
        ImageWriter writer = null;
        ImageOutputStream ios = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try{
            writer = ImageIO.getImageWritersByFormatName("jpg").next();
            ios = new MemoryCacheImageOutputStream(baos);
            writer.setOutput(ios);
            ImageWriteParam param = writer.getDefaultWriteParam();
            param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
            param.setCompressionQuality(compressionQuality);
            writer.write(null, new javax.imageio.IIOImage(originalImage, null, null), param);
            byteArrayInputStream = new ByteArrayInputStream(baos.toByteArray());
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            byteArrayInputStream.close();
            baos.close();
            ios.close();
            writer.dispose();
        }
        return byteArrayInputStream;
    }
 
    /**
     * inputStream 转 byte
     * @param inputStream
     * @return
     * @throws IOException
     */
    public static byte[] readInputStreamToByteArray(InputStream inputStream) throws IOException {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int bytesRead;
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            byteArrayOutputStream.write(buffer, 0, bytesRead);
        }
        return byteArrayOutputStream.toByteArray();
    }
 
 
 
}