• 使用wkhtmltoimage实现生成长图分享


    需求

    用户可以选择以长图的形式分享本网页

    方法

    • wkhtmltopdf
      • wkhtmltopdf url file
      • wkhtmltoimage url file
    • java
      • Runtime.getRuntime().exec()

    下载

    直接去官网下载对应的版本:官网

    命令行使用WK

    >  wkhtmltopdf https://www.nowcoder.com /opt/project/java/mycommunity-pdfs/1.pdf            
    Loading pages (1/6)
    Counting pages (2/6)
    Resolving links (4/6)
    Loading headers and footers (5/6)
    Printing pages (6/6)
    Done
    
    >  wkhtmltoimage  https://www.nowcoder.com /opt/project/java/mycommunity-images/1.png     
    Loading page (1/2)
    Rendering (2/2)
    Done
    
    // 上面那条命令生成的长图太大了,可以使用下面这条:以75%的质量输出
    >  wkhtmltoimage --quality 75  https://www.nowcoder.com /opt/project/java/mycommunity-images/1.png     
    Loading page (1/2)
    Rendering (2/2)
    Done
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在java中调用

    application.properties:

    mycommunity.path.domain=http://localhost:8080
    server.servlet.context-path=/myCommunity
    # WK
    wk.image.command=/usr/local/bin/wkhtmltoimage
    wk.image.storage=/opt/project/java/mycommunity-images/
    
    • 1
    • 2
    • 3
    • 4
    • 5

    WK配置类,在每次程序开始运行时自动生成存放图像的文件夹

    @Configuration
    public class WkConfig {
    
        private static final Logger logger = LoggerFactory.getLogger(WkConfig.class);
    
        @Value("${wk.image.storage}")
        private String wkImageStorage;
    
        @PostConstruct
        public void init(){
            // create the dic about WKimage
            File file = new File(wkImageStorage);
            if(!file.exists()){
                file.mkdir();
                logger.info("create the dictionary of WKimage: " + wkImageStorage);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    调用WK的控制层,因为生成长图比较耗时,所以使用异步操作,在用户操作时调用Kafka的生产者生成事件,通知消费者:

    @Controller
    public class ShareController implements CommunityConstant {
    
        private static final Logger logger = LoggerFactory.getLogger(ShareController.class);
    
        @Autowired
        private EventProducer eventProducer;
    
        @Value("${mycommunity.path.domain}")
        private String domain;
    
        @Value("${server.servlet.context-path}")
        private String contextPath;
    
        @Value("${wk.image.storage}")
        private String wkImageStorage;
    
        @GetMapping(path = "/share")
        @ResponseBody
        public String share(String htmlUrl){
            // generate the file name
            String fileName = CommunityUtil.generateUUID();
    
            // Asynchronous generation long pic
            Event event = new Event()
                    .setTopic(TOPIC_SHARE)
                    .setData("htmlUrl", htmlUrl)
                    .setData("fileName", fileName)
                    .setData("suffix", ".png");
            eventProducer.fireEvent(event);
    
            Map<String, Object> map = new HashMap<>();
            map.put("shareUrl", domain + contextPath + "/share/image" + fileName);
    
            return CommunityUtil.getJSONString(0, null, map);
        }
    
        //
        @GetMapping(path = "/share/image/{fileName}")
        public void getShareImage(@PathVariable("fileName") String fileName, HttpServletResponse response){
            if(StringUtils.isBlank(fileName)){
                throw new IllegalStateException("the file name cannot be blank");
            }
    
            response.setContentType("image/png");
            File file = new File(wkImageStorage + "/" + fileName + ".png");
            try {
                OutputStream os = response.getOutputStream();
                FileInputStream fis = new FileInputStream(file);
                byte[] data = new byte[1024];
                int len = 0;
                while ((len = fis.read(data)) != -1) {
                    os.write(data, 0, len);
                }
            } catch (IOException e) {
                logger.error("querty the long image failed: ", e.getMessage());
            }
        }
    }
    
    • 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

    Kafka的消费者,定义如何消费生成长图的事件:

    @Component
    public class EventConsumer implements CommunityConstant {
    
        @Autowired
        private static final Logger logger = LoggerFactory.getLogger(EventConsumer.class);
    
        @Value("${wk.image.storage}")
        private String wkImageStorage;
    
        @Value("${wk.image.command}")
        private String wkImageCommand;
    
        @KafkaListener(topics = {TOPIC_SHARE})
        public void handleShareMessage(ConsumerRecord record) {
            if (record == null || record.value() == null) {
                logger.error("the content of the message is empty");
                return;
            }
    
            Event event = JSONObject.parseObject(record.value().toString(), Event.class);
            if (event == null) {
                logger.error("message format error");
                return;
            }
    
            String htmlUrl = (String) event.getData().get("htmlUrl");
            String fileName = (String) event.getData().get("fileName");
            String suffix = (String) event.getData().get("suffix");
    
            String cmd = wkImageCommand + " --quality 75 " + htmlUrl + " " + wkImageStorage + "/" + fileName + suffix;
            try {
                Runtime.getRuntime().exec(cmd);
                logger.info("generate long image successfully: " + cmd);
            } catch (IOException e) {
                logger.info("generate long image fail: " + e.getMessage());
            }
        }
    }
    
    • 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

    测试

    在这里插入图片描述

  • 相关阅读:
    Docker的安装
    jupyterlab教程
    echarts 类目轴设置xAxis.interval不起效果
    【Azure 应用服务】Azure JS Function 异步方法中执行SQL查询后,Callback函数中日志无法输出问题
    I2C总线 | I2C总线介绍
    2020研究生数学建模竞赛C题——面向康复工程的脑电信号分析和判别模型——论文研读
    5. 吴恩达深度学习--搭建卷积神经网络模型以及应用
    软考高级-系统架构师-案例分析-案例题1
    [Diffusion Model笔记] DDPM数学推导版 2024.04.23
    XLSX.utils.sheet_to_json() 数字格式转为字符串格式
  • 原文地址:https://blog.csdn.net/weixin_44609676/article/details/132591082