| | |
| | | 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; |
| | |
| | | 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; |
| | |
| | | |
| | | private QrCodeImageUtils() { |
| | | } |
| | | |
| | | /** 批量导出二维码数量上限(防止一次性生成过多导致内存/响应过大) */ |
| | | public static final int MAX_QR_EXPORT = 2000; |
| | | |
| | | /** 二维码边长 */ |
| | | private static final int QR_SIZE = 260; |
| | |
| | | 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(); |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 生成单张标签并写入 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(); |
| | | } |
| | | } |
| | | |
| | | // ==================== 私有方法 ==================== |
| | | |
| | | /** |