springboot集成thymeleaf实战

打印 上一主题 下一主题

主题 210|帖子 210|积分 630

弁言

笔者最近接到一个打印标签的需求,由于之前没有做过类似的功能,以是这也是一次学习探索的机会了,打印的结果图如下:

这个最终的打印是放在58mm*58mm的小标签纸上,条形码就是下面的35165165qweqweqe序列号天生的,也是图片形式。序列号应该放在条形码的正下方居中位置的,但是由于笔者前端技术有点拉跨,遇到样式啥的就头疼,这也是尽力后的结果了。下面看集成过程吧。
一、引入pom相关依靠包

笔者的情况是JDK17,pom相关版本如下,详细用什么版本不固定,不报错就行。
  1. <dependency>
  2.             <groupId>org.springframework.boot</groupId>
  3.             <artifactId>spring-boot-starter-thymeleaf</artifactId>
  4.         </dependency>
  5.         <dependency>
  6.             <groupId>com.google.zxing</groupId>
  7.             <artifactId>core</artifactId>
  8.             <version>3.4.1</version>
  9.         </dependency>
  10.         <dependency>
  11.             <groupId>com.google.zxing</groupId>
  12.             <artifactId>javase</artifactId>
  13.             <version>3.4.1</version>
  14.         </dependency>
  15.         <dependency>
  16.             <groupId>ognl</groupId>
  17.             <artifactId>ognl</artifactId>
  18.             <version>3.4.3</version>
  19.         </dependency>
  20.         <!-- Flying Saucer -->
  21.         <dependency>
  22.             <groupId>org.xhtmlrenderer</groupId>
  23.             <artifactId>flying-saucer-pdf</artifactId>
  24.             <version>9.1.20</version>
  25.         </dependency>
  26.         <!--itext-->
  27.         <dependency>
  28.             <groupId>com.lowagie</groupId>
  29.             <artifactId>itext</artifactId>
  30.             <version>2.1.7</version>
  31.         </dependency>
  32.         <dependency>
  33.             <groupId>org.apache.commons</groupId>
  34.             <artifactId>commons-lang3</artifactId>
  35.             <version>3.12.0</version>
  36.         </dependency>
  37.         <dependency>
  38.             <groupId>org.projectlombok</groupId>
  39.             <artifactId>lombok</artifactId>
  40.             <scope>provided</scope>
  41.         </dependency>
复制代码
二、条形码工具类

  1. package com.hulei.thymeleafproject;
  2. import com.google.zxing.BarcodeFormat;
  3. import com.google.zxing.EncodeHintType;
  4. import com.google.zxing.client.j2se.MatrixToImageWriter;
  5. import com.google.zxing.common.BitMatrix;
  6. import com.google.zxing.oned.Code128Writer;
  7. import org.apache.commons.lang3.StringUtils;
  8. import javax.imageio.ImageIO;
  9. import java.awt.*;
  10. import java.awt.image.BufferedImage;
  11. import java.io.ByteArrayOutputStream;
  12. import java.io.IOException;
  13. import java.util.HashMap;
  14. import java.util.Map;
  15. /**
  16. * @author hulei
  17. * @Date 2024/7/26 14:15
  18. * @Description: 条形码工具类
  19. *
  20. */
  21. public class BarCodeUtils {
  22.     /**
  23.      * 默认图片宽度
  24.      */
  25.     private static final int DEFAULT_PICTURE_WIDTH = 400;
  26.     /**
  27.      * 默认图片高度
  28.      */
  29.     private static final int DEFAULT_PICTURE_HEIGHT = 200;
  30.     /**
  31.      * 默认条形码宽度
  32.      */
  33.     private static final int DEFAULT_BAR_CODE_WIDTH = 300;
  34.     /**
  35.      * 默认条形码高度
  36.      */
  37.     private static final int DEFAULT_BAR_CODE_HEIGHT = 30;
  38.     /**
  39.      * 默认字体大小
  40.      */
  41.     private static final int DEFAULT_FONT_SIZE = 15;
  42.     /**
  43.      * 图片格式
  44.      */
  45.     private static final String FORMAT = "png";
  46.     /**
  47.      * 字符集
  48.      */
  49.     private static final String CHARSET = "utf-8";
  50.     /**
  51.      * 设置 条形码参数
  52.      */
  53.     private static final Map<EncodeHintType, Object> hints = new HashMap<>();
  54.     static {
  55.         hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
  56.     }
  57.     /**
  58.      * 获取条形码图片
  59.      *
  60.      * @param codeValue 条形码内容
  61.      * @return 条形码图片
  62.      */
  63.     public static BufferedImage getBarCodeImage(String codeValue) {
  64.         return getBarCodeImage(codeValue, DEFAULT_BAR_CODE_WIDTH, DEFAULT_BAR_CODE_HEIGHT);
  65.     }
  66.     /**
  67.      * 获取条形码图片
  68.      *
  69.      * @param codeValue 条形码内容
  70.      * @param width     宽度
  71.      * @param height    高度
  72.      * @return 条形码图片
  73.      */
  74.     public static BufferedImage getBarCodeImage(String codeValue, int width, int height) {
  75.         // CODE_128是最常用的条形码格式
  76.         return getBarCodeImage(codeValue, width, height, BarcodeFormat.CODE_128);
  77.     }
  78.     /**
  79.      * 获取条形码图片
  80.      *
  81.      * @param codeValue     条形码内容
  82.      * @param width         宽度
  83.      * @param height        高度
  84.      * @param barcodeFormat 条形码编码格式
  85.      * @return 条形码图片
  86.      */
  87.     public static BufferedImage getBarCodeImage(String codeValue, int width, int height, BarcodeFormat barcodeFormat) {
  88.         Code128Writer writer = switch (barcodeFormat) {
  89.             case CODE_128 ->
  90.                 // 最常见的条形码,但是不支持中文
  91.                     new Code128Writer();
  92.             case PDF_417 ->
  93.                 // 支持中文的条形码格式
  94.                     new Code128Writer();
  95.             // 如果使用到其他格式,可以在这里添加
  96.             default -> new Code128Writer();
  97.         };
  98.         // 编码内容, 编码类型, 宽度, 高度, 设置参数
  99.         BitMatrix bitMatrix;
  100.         bitMatrix = writer.encode(codeValue, barcodeFormat, width, height, hints);
  101.         return MatrixToImageWriter.toBufferedImage(bitMatrix);
  102.     }
  103.     /**
  104.      * 获取条形码
  105.      *
  106.      * @param codeValue 条形码内容
  107.      * @param bottomStr 底部文字
  108.      */
  109.     public static BufferedImage getBarCodeWithWords(String codeValue, String bottomStr) {
  110.         return getBarCodeWithWords(codeValue, bottomStr, "", "", "");
  111.     }
  112.     /**
  113.      * 获取条形码
  114.      * @param codeValue   条形码内容
  115.      * @param bottomStr   底部文字
  116.      * @param topLeftStr  左上角文字
  117.      * @param topRightStr 右上角文字
  118.      */
  119.     public static BufferedImage getBarCodeWithWords(String codeValue,
  120.                                                     String bottomStr,
  121.                                                     String bottomStr2,
  122.                                                     String topLeftStr,
  123.                                                     String topRightStr) {
  124.         return getCodeWithWords(getBarCodeImage(codeValue),
  125.                 bottomStr,
  126.                 bottomStr2,
  127.                 topLeftStr,
  128.                 topRightStr,
  129.                 DEFAULT_PICTURE_WIDTH,
  130.                 DEFAULT_PICTURE_HEIGHT,
  131.                 0,
  132.                 -20,
  133.                 0,
  134.                 0,
  135.                 0,
  136.                 0,
  137.                 DEFAULT_FONT_SIZE);
  138.     }
  139.     /**
  140.      * 获取条形码
  141.      *
  142.      * @param codeImage       条形码图片
  143.      * @param firstBottomStr  底部文字首行
  144.      * @param secondBottomStr 底部文字次行
  145.      * @param topLeftStr      左上角文字
  146.      * @param topRightStr     右上角文字
  147.      * @param pictureWidth    图片宽度
  148.      * @param pictureHeight   图片高度
  149.      * @param codeOffsetX     条形码宽度
  150.      * @param codeOffsetY     条形码高度
  151.      * @param topLeftOffsetX  左上角文字X轴偏移量
  152.      * @param topLeftOffsetY  左上角文字Y轴偏移量
  153.      * @param topRightOffsetX 右上角文字X轴偏移量
  154.      * @param topRightOffsetY 右上角文字Y轴偏移量
  155.      * @param fontSize        字体大小
  156.      * @return 条形码图片
  157.      */
  158.     public static BufferedImage getCodeWithWords(BufferedImage codeImage,
  159.                                                  String firstBottomStr,
  160.                                                  String secondBottomStr,
  161.                                                  String topLeftStr,
  162.                                                  String topRightStr,
  163.                                                  int pictureWidth,
  164.                                                  int pictureHeight,
  165.                                                  int codeOffsetX,
  166.                                                  int codeOffsetY,
  167.                                                  int topLeftOffsetX,
  168.                                                  int topLeftOffsetY,
  169.                                                  int topRightOffsetX,
  170.                                                  int topRightOffsetY,
  171.                                                  int fontSize) {
  172.         BufferedImage picImage = new BufferedImage(pictureWidth, pictureHeight, BufferedImage.TYPE_INT_RGB);
  173.         Graphics2D g2d = picImage.createGraphics();
  174.         // 抗锯齿
  175.         setGraphics2D(g2d);
  176.         // 设置白色
  177.         setColorWhite(g2d, picImage.getWidth(), picImage.getHeight());
  178.         // 条形码默认居中显示
  179.         int codeStartX = (pictureWidth - codeImage.getWidth()) / 2 + codeOffsetX;
  180.         int codeStartY = (pictureHeight - codeImage.getHeight()) / 2 + codeOffsetY;
  181.         // 画条形码到新的面板
  182.         g2d.drawImage(codeImage, codeStartX, codeStartY, codeImage.getWidth(), codeImage.getHeight(), null);
  183.         // 画文字到新的面板
  184.         g2d.setColor(Color.BLACK);
  185.         // 字体、字型、字号
  186.         g2d.setFont(new Font("微软雅黑", Font.PLAIN, fontSize));
  187.         // 文字与条形码之间的间隔
  188.         int wordAndCodeSpacing1 = 0;
  189.         if (StringUtils.isNotEmpty(firstBottomStr)) {
  190.             // 文字长度
  191.             int strWidth = g2d.getFontMetrics().stringWidth(firstBottomStr);
  192.             // 文字X轴开始坐标,这里是居中
  193.             int strStartX = codeStartX + (codeImage.getWidth() - strWidth) / 2;
  194.             // 文字Y轴开始坐标
  195.             int strStartY = codeStartY + codeImage.getHeight() + fontSize + wordAndCodeSpacing1;
  196.             // 画文字
  197.             g2d.drawString(firstBottomStr, strStartX, strStartY);
  198.         }
  199.         // 文字与条形码之间的间隔
  200.         int wordAndCodeSpacing2 = 30;
  201.         if (StringUtils.isNotEmpty(secondBottomStr)) {
  202.             // 文字长度
  203.             int strWidth = g2d.getFontMetrics().stringWidth(secondBottomStr);
  204.             // 文字X轴开始坐标,这里是居中
  205.             int strStartX = codeStartX + (codeImage.getWidth() - strWidth) / 2;
  206.             // 文字Y轴开始坐标
  207.             int strStartY = codeStartY + codeImage.getHeight() + fontSize + wordAndCodeSpacing2;
  208.             // 画文字
  209.             g2d.drawString(secondBottomStr, strStartX, strStartY);
  210.         }
  211.         if (StringUtils.isNotEmpty(topLeftStr)) {
  212.             // 文字长度
  213.             int strWidth = g2d.getFontMetrics().stringWidth(topLeftStr);
  214.             // 文字X轴开始坐标
  215.             int strStartX = codeStartX + topLeftOffsetX;
  216.             // 文字Y轴开始坐标
  217.             int strStartY = codeStartY + topLeftOffsetY - wordAndCodeSpacing1;
  218.             // 画文字
  219.             g2d.drawString(topLeftStr, strStartX, strStartY);
  220.         }
  221.         if (StringUtils.isNotEmpty(topRightStr)) {
  222.             // 文字长度
  223.             int strWidth = g2d.getFontMetrics().stringWidth(topRightStr);
  224.             // 文字X轴开始坐标,这里是居中
  225.             int strStartX = codeStartX + codeImage.getWidth() - strWidth + topRightOffsetX;
  226.             // 文字Y轴开始坐标
  227.             int strStartY = codeStartY + topRightOffsetY - wordAndCodeSpacing1;
  228.             // 画文字
  229.             g2d.drawString(topRightStr, strStartX, strStartY);
  230.         }
  231.         g2d.dispose();
  232.         picImage.flush();
  233.         return picImage;
  234.     }
  235.     /**
  236.      * 设置 Graphics2D 属性  (抗锯齿)
  237.      *
  238.      * @param g2d Graphics2D提供对几何形状、坐标转换、颜色管理和文本布局更为复杂的控制
  239.      */
  240.     private static void setGraphics2D(Graphics2D g2d) {
  241.         g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
  242.         g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_DEFAULT);
  243.         Stroke s = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER);
  244.         g2d.setStroke(s);
  245.     }
  246.     /**
  247.      * 设置背景为白色
  248.      *
  249.      * @param g2d Graphics2D提供对几何形状、坐标转换、颜色管理和文本布局更为复杂的控制
  250.      */
  251.     private static void setColorWhite(Graphics2D g2d, int width, int height) {
  252.         g2d.setColor(Color.WHITE);
  253.         //填充整个屏幕
  254.         g2d.fillRect(0, 0, width, height);
  255.         //设置笔刷
  256.         g2d.setColor(Color.BLACK);
  257.     }
  258.     /**
  259.      * 将 BufferedImage 转为 base64
  260.      */
  261.     public static String bufferedImage2Base64(BufferedImage image) throws IOException {
  262.         // 输出流
  263.         ByteArrayOutputStream stream = new ByteArrayOutputStream();
  264.         ImageIO.write(image, FORMAT, stream);
  265.         java.util.Base64.Encoder encoder = java.util.Base64.getEncoder();
  266.         String imgBase64 = new String(encoder.encode(stream.toByteArray()), CHARSET);
  267.         imgBase64 = "data:image/" + FORMAT + ";base64," + imgBase64;
  268.         return imgBase64;
  269.     }
  270. }
复制代码
这个工具类中,默认天生的条形码图片格式是png,固然可以自己修改格式。
三、thymeleaf画模板

这个就是打印模板了,thymeleaf和freemarker一样都是模板引擎,freemarker模板语法更简单些。如果需要简单的变量替换和循环,FreeMarker大概是更好的选择。如果需要更丰富的模板功能和动态内容处理,Thymeleaf大概更得当。笔者这里选择的是thymeleaf。
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4.     <title>维修库商品打印标签模板</title>
  5.     <meta charset="UTF-8"></meta>
  6.     <style>        body, html {
  7.         margin: 0;
  8.         padding: 0;
  9.         width: 70mm;
  10.         height: 70mm;
  11.         font-family: 'SimSun', sans-serif; /* 防止生成的PDF中文不显示 */
  12.     }
  13.     h1 {
  14.         text-align: center;
  15.         font-size: 12px;
  16.         line-height: 1.5;
  17.     }
  18.     p {
  19.         font-size: 12px;
  20.         margin: 3px 0;
  21.     }
  22.     .device-code {
  23.         display: flex; /* 使用Flexbox布局 */
  24.         align-items: center; /* 垂直居中对齐 */
  25.     }
  26.     .sn-container {
  27.         display: inline-flex; /* 内联Flexbox容器 */
  28.         align-items: center; /* 垂直居中对齐 */
  29.         margin-left: 2px; /* 与“设备码:”之间的间距 */
  30.     }
  31.     .sn-image {
  32.         width: auto; /* 图片宽度自适应 */
  33.     }
  34.     .sn-text {
  35.         margin-top: 5px; /* 文本与图片之间的间距 */
  36.         text-align: center; /* 文字居中 */
  37.     }
  38.     img {
  39.         vertical-align: middle;
  40.         display: inline-block;
  41.     }
  42.     </style>
  43. </head>
  44. <body>
  45. <div>
  46.     <h1>
  47.         <img th:src="${zlbcImage}" alt="Image" style="height:30px;"></img>智链泊车
  48.     </h1>
  49.     <p th:text="${createTime != null ? '入库日期:'+ createTime : '入库日期:未知'}"></p>
  50.     <p th:text="${materialName != null ? '名&nbsp;&nbsp;&nbsp;&nbsp;称:'+ materialName : '名称:未知'}"></p>
  51.     <p th:text="${supplierName != null ? '客&nbsp;&nbsp;&nbsp;&nbsp;户:'+ supplierName : '客户:未知'}"></p>
  52.     <p class="device-code">
  53.         设&nbsp;备&nbsp;码:
  54.         <span class="sn-container">
  55.         <img class="sn-image" th:src="${sequencesNumberImage}" alt="Image"/>
  56.         <div class="sn-text" th:text="${sequencesNumber}">${sequencesNumber}</div>
  57.     </span>
  58.     </p>
  59. </div>
  60. </body>
  61. </html>
复制代码
这个模板里面的变量赋值时比较简单的,主要是有两个图片的变量zlbcImagesequencesNumberImage,一个是聪明停车前面的原型小图标,一个就是条形码是,在赋值时是需要把图片读成BufferedImage,再把BufferedImage使用base64编码一下。
另外一个紧张的点是:font-family: ‘SimSun’, sans-serif;
这个属性必须加上,否则背面把html转成PDF时,中文会不表现。
四、字体准备simsun.ttc

这个字体是因为,我要把html转成一个PDF,中心转换需要一些字体,并且支持中文,网上搜索了下,选择了simsun.ttc这个字体,同时我在html上也指定了这个字体。网上下载这个字体资源库后,放在如下位置,以便程序中加载使用。

五、测试代码

  1. package com.hulei.thymeleafproject;
  2. import com.lowagie.text.pdf.BaseFont;
  3. import jakarta.annotation.Resource;
  4. import org.springframework.web.bind.annotation.PostMapping;
  5. import org.springframework.web.bind.annotation.RequestBody;
  6. import org.springframework.web.bind.annotation.RestController;
  7. import org.thymeleaf.TemplateEngine;
  8. import org.thymeleaf.context.Context;
  9. import org.xhtmlrenderer.pdf.ITextFontResolver;
  10. import org.xhtmlrenderer.pdf.ITextRenderer;
  11. import javax.imageio.ImageIO;
  12. import java.awt.image.BufferedImage;
  13. import java.io.*;
  14. import java.nio.file.Files;
  15. import java.nio.file.Path;
  16. import java.nio.file.Paths;
  17. import java.text.SimpleDateFormat;
  18. import java.util.*;
  19. import java.util.List;
  20. /**
  21. * @author hulei
  22. * @date 2024/7/27 9:26
  23. */
  24. @RestController
  25. public class TestController {
  26.     @Resource
  27.     private TemplateEngine templateEngineBySelf;
  28.     @PostMapping("/printSNLabel")
  29.     public void test(@RequestBody List<PrintSNLabelReqDTO> list) {
  30.         list.forEach(loop -> {
  31.             Map<String, Object> map = new HashMap<>();
  32.             map.put("createTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
  33.             map.put("materialName", loop.getMaterialName());
  34.             map.put("supplierName", loop.getSupplierName());
  35.             //设备码图片二进制字节流
  36.             BufferedImage sequencesNumberImage = BarCodeUtils.getBarCodeImage(loop.getSequencesNumber(), 100, 50);
  37.             this.storeImage(sequencesNumberImage, "E:/111.png");
  38.             try {
  39.                 String base64Image = BarCodeUtils.bufferedImage2Base64(sequencesNumberImage);
  40.                 System.out.println("base64Image: " + base64Image);
  41.                 map.put("sequencesNumberImage", base64Image);
  42.             } catch (IOException e) {
  43.                 throw new RuntimeException(e);
  44.             }
  45.             map.put("sequencesNumber", loop.getSequencesNumber());
  46.             try {
  47.                 ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
  48.                 String symbolImagePath = "images/zlbcImage.png";
  49.                 InputStream inputStream = classLoader.getResourceAsStream(symbolImagePath);
  50.                 assert inputStream != null;
  51.                 BufferedImage zlbcImageBufferedImage = ImageIO.read(inputStream);
  52.                 this.storeImage(zlbcImageBufferedImage, "E:/222.png");
  53.                 String zlbcImage = BarCodeUtils.bufferedImage2Base64(zlbcImageBufferedImage);
  54.                 System.out.println("zlbcImage: " + zlbcImage);
  55.                 map.put("zlbcImage", zlbcImage);
  56.             } catch (IOException e) {
  57.                 throw new RuntimeException(e);
  58.             }
  59.             try {
  60.                 generateSNPicture(map);
  61.             } catch (IOException e) {
  62.                 throw new RuntimeException(e);
  63.             }
  64.         });
  65.     }
  66.     private void generateSNPicture(Map<String,Object> map) throws IOException {
  67.         // 填充模板数据
  68.         Context context = new Context();
  69.         context.setVariable("createTime", map.get("createTime"));
  70.         context.setVariable("materialName", map.get("materialName"));
  71.         context.setVariable("supplierName", map.get("supplierName"));
  72.         context.setVariable("sequencesNumberImage", map.get("sequencesNumberImage"));
  73.         context.setVariable("sequencesNumber", map.get("sequencesNumber"));
  74.         context.setVariable("zlbcImage", map.get("zlbcImage"));
  75.         String htmlContent = templateEngineBySelf.process("printTemplate", context);
  76.         System.out.println(htmlContent);
  77.         htmlToPdf(htmlContent);
  78.     }
  79.     private void htmlToPdf(String htmlContent){
  80.         try {
  81.             //创建PDf文件
  82.             ITextRenderer renderer = new ITextRenderer();
  83.             //获取使用的字体数据(由于对中文字体显示可能会不支持,所以需要主动添加字体数据设置。)
  84.             ITextFontResolver fontResolver = renderer.getFontResolver();
  85.             fontResolver.addFont("templates/fonts/simsun.ttc",BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
  86.             //设置文件名称
  87.             String sDate = new SimpleDateFormat("yyyyMMdd").format(new Date());
  88.             String sTime = new SimpleDateFormat("HHmmssSSS").format(new Date());
  89.             // 生成临时文件
  90.             Path tempPdfPath = Files.createTempFile("temp_pdf_"+sDate+sTime, ".pdf");
  91.             String pdfFilePath = tempPdfPath.toAbsolutePath().toString();
  92.             // 将html生成文档
  93.             renderer.setDocumentFromString(htmlContent);
  94.             renderer.layout();
  95.             OutputStream os = new FileOutputStream(pdfFilePath);
  96.             // 将文档写入到输出流中
  97.             renderer.createPDF(os);
  98.             // 关闭流
  99.             os.close();
  100.             //把临时生成的文件转移到E盘,这里可以根据个人需求选在把临时文件上传到文件服务器
  101.             System.out.println("pdfFilePath: "+pdfFilePath);
  102.             File tempPdfFile = tempPdfPath.toFile();
  103.             System.out.println("tempPdfFileName: "+tempPdfFile.getName());
  104.             // 复制文件到E盘
  105.             try {
  106.                 Path targetPath = Paths.get("E:", tempPdfFile.getName()); // 目标路径
  107.                 Files.copy(tempPdfPath, targetPath);
  108.                 System.out.println("文件已复制到 E 盘");
  109.             } catch (Exception e) {
  110.                 System.err.println("复制文件时发生错误: " + e.getMessage());
  111.             }
  112.             //删除临时生成的本地PDF文件
  113.             Files.delete(tempPdfPath);
  114.         } catch (Exception e) {
  115.             System.out.println("生成pdf文件失败");
  116.             throw new RuntimeException(e);
  117.         }
  118.     }
  119.     private void storeImage(BufferedImage image, String filePath){
  120.         try {
  121.             // 指定输出文件路径和格式
  122.             File outputFile = new File(filePath);
  123.             // 使用 ImageIO.write 方法将图片写入磁盘
  124.             boolean isWritten = ImageIO.write(image, "png", outputFile);
  125.             if (isWritten) {
  126.                 System.out.println("图片已成功保存到磁盘.");
  127.             } else {
  128.                 System.out.println("图片保存失败.");
  129.             }
  130.         } catch (IOException e) {
  131.             System.err.println("保存图片时发生错误: " + e.getMessage());
  132.         }
  133.     }
  134. }
复制代码
这里为了展示代码,没有分层了,全都放在了controller层。主要分为三块:加载html模板,变量赋值,html转pdf
转成pdf后的结果如下:

Apifox测试工具,测试数据如下,留意json是数组形式,因为后端controller接收的是List

整个代码我已上传到gitee:gitee堆栈地址

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

张裕

高级会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表