Loading

html代码转为图片

方法:

使用wkhtmltopdf工具。

代码实现:

1、创建条件类

@ApiModel(value = "html转imageDTO")
public class HtmlToImageDTO {

    /**
     * html代码
     */
    @ApiModelProperty(value = "html代码")
    private String htmlCode;

    /**
     * wkhtmltopdf命令
     * 格式:1、命令参数,值;命令参数,值
     *      2、命令参数,值;命令参数
     *      3、命令参数;命令参数
     */
    @ApiModelProperty(value = "wkhtmltopdf命令")
    private String commandParam;

    public String getHtmlCode() {
        return htmlCode;
    }

    public void setHtmlCode(String htmlCode) {
        this.htmlCode = htmlCode;
    }

    public String getCommandParam() {
        return commandParam;
    }

    public void setCommandParam(String commandParam) {
        this.commandParam = commandParam;
    }
}

2、接口实现

    /**
     * wkhtmltopdfPath安装路径
     */
    @Value("${service.wkhtmltoimagePath}")
    private String wkhtmltoimagePath;


    public String generateImageFromHtml(HtmlToImageDTO data) {
        String fileId = null;
        String htmlCode = data.getHtmlCode();
        String commandParam = data.getCommandParam() == null ? "" : data.getCommandParam();
        //生成文件名称
        String filePre = File.separator + DateUtil.format(new Date(), "yyyyMMddHHmmss") + UUID.randomUUID().toString(true);
        String htmlFileName = filePre + ".html";
        String imageFileName = filePre + ".png";
        //生成文件路径
        File pathDirectory = new File(wkhtmltoimagePath);
        if(!pathDirectory.exists() && !pathDirectory.mkdirs()){
            LOGGER.error("htmlCode转image--文件夹创建失败!");
        }
        String htmlFilePath = wkhtmltoimagePath + htmlFileName;
        String imageFilePath = wkhtmltoimagePath + imageFileName;
        //wkhtmltopdf命令
        File htmlFile = new File(htmlFilePath);
        try {
            if(htmlFile.exists()){
                htmlFile.delete();
            }
            htmlFile.createNewFile();
            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(
                    Files.newOutputStream(htmlFile.toPath()), StandardCharsets.UTF_8));
            bufferedWriter.write(htmlCode);
            bufferedWriter.close();
            //执行命令
            List<String> command = getCommand(commandParam, htmlFilePath, imageFilePath);
            Util.executeCommand(command);
            LOGGER.info("htmlCode转image--执行命令:" + command);
            File imageFile = new File(imageFilePath);
            //如果图片文件不存在,表示生成失败
            if(!imageFile.exists()){
                htmlFile.delete();
                LOGGER.error("htmlCode转image--生成image图片失败!");
                return null;
            }
            BufferedImage bufferedImage = ImageIO.read(imageFile);
            //解决图片透明底变白问题
            int height = bufferedImage.getHeight();
            int width = bufferedImage.getWidth();
            BufferedImage newImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2d = newImage.createGraphics();
            g2d.drawImage(bufferedImage, 0, 0, null);
            g2d.dispose();
            ByteArrayOutputStream imageOutPutStream = new ByteArrayOutputStream();
            ImageIO.write(newImage, "png", imageOutPutStream);
            InputStream inputStream = new ByteArrayInputStream(imageOutPutStream.toByteArray());
            FileItem fileItem = FileUtil.createFileItem(inputStream, "file.png");
            //上传到文件服务,返回文件ID
            fileId = getFileId(fileItem);
            imageOutPutStream.close();
            inputStream.close();

            //销毁生成的文件
            htmlFile.delete();
            imageFile.delete();
        }catch (Exception e){
            LOGGER.error("htmlCode转image--" + e.getMessage());
        }
        return fileId;
    }

    private List<String> getCommand(String commandParam, String htmlPath, String imagePath){
        List<String> commandList = new ArrayList<>();
        commandList.add("wkhtmltoimage");
        if(StringUtils.isNotBlank(commandParam)){
            String[] allCommandArray = commandParam.split(";");
            for (String commandStr : allCommandArray) {
                String[] commandArray = commandStr.split(",");
                commandList.add(commandArray[0]);
                if(commandArray.length == 2) {
                    commandList.add(commandArray[1]);
                }
            }
        }
        commandList.add(htmlPath);
        commandList.add(imagePath);
        return commandList;
    }

3、命令执行方法

  public static void executeCommand(List<String> cmd) {
        ProcessBuilder pb = new ProcessBuilder(cmd);
        pb.redirectErrorStream(true);
        Process process = null;
        ProcessOutputThread processOutput = null;
        try {
            process = pb.start();
            processOutput = new ProcessOutputThread(process);
            processOutput.start();
            Worker worker = new Worker(process);
            worker.start();
            try {
                // ExceptionLogUtil.infoLog("进入超时判断线程:"+worker.getName());
                worker.join(10000);
            } catch (Exception e) {
                processOutput.interrupt();
                worker.interrupt();
            }
            if (worker.exit == null) {
                processOutput.interrupt();
                worker.interrupt();
                // ExceptionLogUtil.infoLog(line);
            }
        } catch (Exception e) {
            // e.printStackTrace();
            // ExceptionLogUtil.saveLog(e);
        } finally {
            if (process != null) {
                process.destroy();
            }
            if (processOutput != null) {
                processOutput.interrupt();
            }
        }
    }

    /**
     * 处理进程 输入流 错误流
     */
    private static class ProcessOutputThread extends Thread {
        /**
         * process
         */
        private final Process process;

       
        private ProcessOutputThread(Process process) {
            this.process = process;
        }

        
        public void run() {
            BufferedReader br = null;
            try {
                br = new BufferedReader(new InputStreamReader(process.getInputStream(), "GBK"));
                String line = null;
                while (null != (line = br.readLine())) {

                }
            } catch (Exception e) {
                if (br != null) {
                    try {
                        br.close();
                    } catch (Exception e2) {
                    }
                }
            } finally {
                if (br != null) {
                    try {
                        br.close();
                    } catch (Exception e2) {
                    }
                }
            }
        }
    }

    /**
     * 超时控制
     */
    private static class Worker extends Thread {
        /**
         * process
         */
        private final Process process;
        /**
         * exit
         */
        private Integer exit;

        /**
         * Worker
         */
        private Worker(Process process) {
            this.process = process;
        }

      
        public void run() {
            try {
                exit = process.waitFor();
            } catch (InterruptedException ignore) {
                return;
            }
        }
    }

tips:

1、wkhtmltopdf运行环境需要安装对应的字体,否则会出现生成图片内容乱码或者空白问题。
2、可以使用wkhtmltoimage - - 来进行标准输入输出流操作,避免创建文件操作。
3、为避免生成的图片产生过多空白,可以使用CSS调整:如:

String head = "<!DOCTYPE html>\n" +
                "<html lang=\"en\">\n" +
                "<head>\n" +
                "    <meta charset=\"UTF-8\">\n" +
                "    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n" +
                "    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n" +
                "    <title>Document</title>\n" +
                "    <style>\n" +"" +
                "       * {\n" +
                "           margin: 0px;\n" +
                "           padding: 0px;\n" +
                "       }"+
                "       body,html {\n" +
                "           height: auto;\n" +
                "        }\n" +
                "       .mainDiv {\n" +
                "           width: 100%;\n" +
                "           height: auto;\n" +
                "        }\n" +
                (style == null ? "" : style) +
                "\n</style></head><body><div class=\"mainDiv\">";
        String fail = "</div></body></html>";
        String htmlCode = head + htmlBody + fail;

4、--transparent命令可以使图片背景变成透明,但是图片格式必须为png

posted @ 2023-05-17 11:32  IamHzc  阅读(4630)  评论(0编辑  收藏  举报