[java] html转图片方案「建议收藏」

[java] html转图片方案「建议收藏」纯java项目后端进行HTML转图片公司有个需求是在小程序将订单信息按一定样式整理后转成图片。客户点击按钮下载后可以将图片保存,并可以直接在微信群里分享。由于时间紧迫的关系,这里前后端并行尝试方案,前端通过canvas方案手动绘制,后端则由我这边进行摸索。原本想把各种尝试过的方案都记录下来,但

大家好,欢迎来到IT知识分享网。

纯java项目后端进行HTML转图片

公司有个需求是在小程序将订单信息按一定样式整理后转成图片。客户点击按钮下载后可以将图片保存,并可以直接在微信群里分享。

由于时间紧迫的关系,这里前后端并行尝试方案,前端通过canvas方案手动绘制,后端则由我这边进行摸索。

 

原本想把各种尝试过的方案都记录下来,但是完成后现在一查,原来已经有人尝试过了,这里就直接他的上图。

[java] html转图片方案「建议收藏」

图片来自https://blog.csdn.net/xuechangchun007/article/details/120936431

 

图中说明过的html2img及cssbox都尝试过,但是样式或多或少都有问题,因为这些存Java实现的基本都是通过g2d模拟绘制来生成图片,其样式和直接在浏览器上看到的肯定会存在差异。

另外我还尝试过github上的开源项目openhtmltopdf,结果都差不多。

需要没有差异的话,就必须使用类似selenium这种无头浏览器,模拟用户查看网站并截图的方案。

 

基于wkhtmltox

虽然我最先考虑过的是selenium,但是由同事推荐,最终我选择了基于wkhtmltox这个方案,这里记录下。

wkhtmltopdf核心点是使用webkit浏览器进行,基本上也是模拟用户在浏览器查看图片并截图保存的方案。

  • window按照只需要https://wkhtmltopdf.org/downloads.html下载对应的window包

然后使用命令调用即可,如下载在D:/1/,那么进入到D:/1/wkhtmltox/bin/目录,打开命令行执行

// 格式
// wkhtmltoimage程序地址 来源地址 保存地址
.\wkhtmltoimage.exe http://baidu.com 1.png

即可将将百度截图为图片保存到当前目录:

 

[java] html转图片方案「建议收藏」

 

 

 对于一个html文件也是一样,若将Html文件移入到bin目录,那么

.\wkhtmltoimage.exe 1.html 1.png
  • 部署到linux需要根据服务器的系统版本下载对应的包并在服务器上安装即可

 方法可参考https://www.cnblogs.com/agang-php/p/14966285.html

 

编码部分

根据需求,我的方案是

  • 创建对应样式的html模板
  • 通过freemarker填充参数,获取html字符串。
  • 保存字符串为html文件到临时文件目录
  • 调用wkhtmltoimage生成图片到本地临时目录
  • 读取图片为byte数组
  • 删除临时文件

 

其中html字符串转图片部分代码可以参考如下

import java.io.File;
import java.io.FileWriter;
import java.nio.file.Files;

import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import lombok.extern.slf4j.Slf4j;

/**
 * HTML转图片的默认实现,采用wkhtmltox实现
 * <p>
 * wkhtmltox基于webkit浏览器将HTML转为图片,需要一个程序来执行,对于window来说是一个无需安装的exe
 * 对于linux来说需要进行安装
 * 
 */
@Slf4j
@Service
public class Html2ImageBizImpl implements Html2ImageBiz {

    /**
     * wkhtmltox的使用命令,对window来讲是exe的地址,对于linux是安装的命令地址(如/usr/local/bin/wkhtmltoimg)
     */
    private String wkcmd;
    /**
     * 用于存放临时文件的文件夹,由于wkhtmltox为系统插件独立进程,只能作为文件操作后再按流进行读取
     */
    private String tmpFilePath;

    @Override
    public byte[] stringToPng(String htmlString) {
        if (EmptyUtil.isEmpty(wkcmd)) {
            throw new UnsupportedOperationException("未配置wkhtmltox的使用命令参数,功能不可用");
        }
        if (EmptyUtil.isEmpty(tmpFilePath)) {
            throw new UnsupportedOperationException("未配置HTML转图片的临时文件目录,功能不可用");
        }
        Assert.hasLength(htmlString, "参数错误,HTML文件没有内容");

        String htmlFileName = null;
        String pngFileName = null;
        try {
            // 存HTML文件
            htmlFileName = saveHtml2File(htmlString);
            // 转为PNG,获取其文件名
            pngFileName = html2Png(htmlFileName);
            // 将PNG读取为bytes
            return readFile2ByteArray(pngFileName);

        } finally {
            // 删除临时文件
            if (htmlFileName != null) {
                removeTmpFile(htmlFileName);
            }
            if (pngFileName != null) {
                removeTmpFile(pngFileName);
            }
        }
    }

    /**
     * 将HTML内容写入文件
     * 
     * @param htmlString HTML文件内容
     */
    private String saveHtml2File(String htmlString) {
        String fileName = getRandomFileName(".html");
        try (FileWriter fileWriter = new FileWriter(new File(fileName))) {
            fileWriter.write(htmlString);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        log.info("保存HTML成功: {}", fileName);
        return fileName;
    }

    /**
     * 使用wkhtmltox将HTML转为PNG(都在硬盘上操作
     * 
     * @param htmlFileName HTML文件路径
     */
    private String html2Png(String htmlFileName) {
        String pngFileName = getRandomFileName(".png");

        StringBuilder cmd = new StringBuilder();
        cmd.append(wkcmd);
        cmd.append(" ");
        cmd.append(htmlFileName);
        cmd.append(" ");
        cmd.append(pngFileName);
        try {
            Process proc = Runtime.getRuntime().exec(cmd.toString());
            proc.getInputStream();
            proc.waitFor();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        log.info("转PNG成功: {}, {}", htmlFileName, pngFileName);
        return pngFileName;
    }

    /**
     * 读取文件二进制数组
     * 
     * @param fileName
     */
    private byte[] readFile2ByteArray(String fileName) {
        File file = new File(fileName);
        try {
            return Files.readAllBytes(file.toPath());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 删除临时文件
     * 
     * @param fileName 文件名
     */
    private void removeTmpFile(String fileName) {
        Assert.hasLength(fileName, "临时文件参数为空");
        File file = new File(fileName);
        if (file.isDirectory()) {
            throw new IllegalArgumentException("不能删除文件夹");
        }
        try {
            Files.delete(file.toPath());
            log.info("删除临时文件成功:{}", fileName);
        } catch (Exception e) {
            log.warn("删除临时文件失败", e);
        }
    }

    /**
     * 构建随机的文件地址(采用IdGenerator)
     * 
     * @param suffix 如.html
     */
    private String getRandomFileName(String suffix) {
        return tmpFilePath + "/" + "h2p" + IdGenerator.nextId() + suffix;
    }
}

这里的wkcmd和tmpFilePath是两个通过可配置的参数,如通过spring的@Value注入或者直接写死。

另外由于部分代码类如IdGenerator,用到了公司项目的包,这里删去引用。直接保存的话,需要替换为近似的方法。


 

END;

 

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/28440.html

(0)
上一篇 2023-09-24 10:45
下一篇 2023-09-24 22:15

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信