云
4 小时以前 a7cf1913c376b53f487f0ef8dfff084c5bc010a7
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/framework/qrcode/QrCodeImageUtils.java
@@ -9,6 +9,9 @@
import lombok.extern.slf4j.Slf4j;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import javax.imageio.stream.MemoryCacheImageOutputStream;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
@@ -17,6 +20,7 @@
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.FilterOutputStream;
import java.util.List;
import java.util.Set;
import java.util.zip.ZipEntry;
@@ -32,9 +36,6 @@
    private QrCodeImageUtils() {
    }
    /** 批量导出二维码数量上限(防止一次性生成过多导致内存/响应过大) */
    public static final int MAX_QR_EXPORT = 2000;
    /** 二维码边长 */
    private static final int QR_SIZE = 260;
@@ -120,10 +121,7 @@
        ZipOutputStream zip = new ZipOutputStream(response.getOutputStream());
        try {
            for (QrLabelInfo label : labels) {
                BufferedImage image = createQrLabel(label.getCode(), label.getFileName());
                zip.putNextEntry(new ZipEntry(label.getFileName() + ".png"));
                ImageIO.write(image, "png", zip);
                zip.closeEntry();
                writeZipEntry(zip, label);
            }
            zip.finish();
            zip.flush();
@@ -132,6 +130,38 @@
        }
    }
    /**
     * 生成单张标签并写入 ZIP 条目
     * <p>
     * 供后台导出任务自建写入循环时复用:调用方需要自行在每张之间检查取消、采样 CPU 并回写进度,
     * 因此这里只负责"一张"的处理,不接管循环。
     *
     * @param zip   ZIP 输出流
     * @param label 标签信息
     */
    public static void writeZipEntry(ZipOutputStream zip, QrLabelInfo label) throws IOException {
        BufferedImage image = createQrLabel(label.getCode(), label.getFileName());
        zip.putNextEntry(new ZipEntry(label.getFileName() + ".png"));
        try {
            // ImageIO.write(OutputStream) 可能为每张图片创建磁盘缓存;大批量导出改用内存缓存。
            // 包装流的 close 仅 flush,不能关闭外层 ZIP。
            ImageWriter writer = ImageIO.getImageWritersByFormatName("png").next();
            try (ImageOutputStream imageOut = new MemoryCacheImageOutputStream(new FilterOutputStream(zip) {
                @Override
                public void close() throws IOException {
                    flush();
                }
            })) {
                writer.setOutput(imageOut);
                writer.write(image);
            } finally {
                writer.dispose();
            }
        } finally {
            zip.closeEntry();
        }
    }
    // ==================== 私有方法 ====================
    /**