使用phantomJS将html转为图片

  • 问题描述

近期写稿项目碰到一个问题,由于文章会发布到不同设备和平台上,在前端展示的时候可能会与平台本身的样式发生覆盖,导致表格样式显示不正常。短时间内想要做出一个适应所有环境的前端样式不太现实。因为使用本地模板生成的表格不存在样式问题,所以考虑将本地html模板中的

标签内容转换为图片并在原位置替换。

  • 解决思路

使用phantomJs模拟浏览器访问html模板,用选择器截取dom节点,获取

标签并把表格放入新建的canvas画布

  • 实施步骤

1.安装phantomjs并配置环境变量

2.使用cmd命令执行phantomjs脚本,使用phantom访问浏览器的脚本

import org.dom4j.DocumentException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * html dom转图片
 *
 * @author Hongyi Zheng
 * @date 2018/7/31
 */
@Component("dom2ImageService")
public class Dom2ImageService {

    @Value("${server.port}")
    private String port;

    @Value("${server.context-path}")
    private String contextPath;

    private static final Logger logger = LoggerFactory.getLogger(Dom2ImageService.class);

    private final ArticleBean articleBean;

    @Autowired
    FileUploadService fileUploadService;

    @Autowired
    PageContentService pageContentService;

    @Autowired
    ArticleTraceService articleTraceService;

    @Autowired
    public Dom2ImageService(ArticleBean articleBean) {
        this.articleBean = articleBean;
    }

    public void convert2Img(String traceId,String tblName,String tblContent) throws IOException {
        logger.info("[" + traceId + "]表格{}转换图片中...", tblName);

        //拼接phantom命令行/参数
        StringBuilder phantomCmd = new StringBuilder();
        String osName = System.getProperties().getProperty("os.name").toLowerCase();
        String tmpPath = articleBean.getD2ImgTemplatePath();
        String htmlName = traceId + tblName + ".html";
        String imgName = traceId + tblName + ".png";
        List list = articleTraceService.selectByTraceId(traceId);
        String tmpName = "";
        if (null != list && list.size() > 0) {
            tmpName = list.get(0).getTempName();
        }
        String date = DateUtils.format(new Date(), DateUtils.STYLE_yyyyMMdd);
        String lxPath = String.format("/opt/app/applications/xxxxx/temp/%s/%s/%s/%s", date, tmpName, "tmp", htmlName);
        String winPath = String.format("%s%s\\%s\\%s\\%s", articleBean.getArticleLocalPath(), date, tmpName, "tmp", htmlName);
        String target;
        String dest;
        if (osName.contains("linux")) {
            target = lxPath;
            dest = String.format("opt/app/applications/xxxxx/temp/%s/%s/%s", date, tmpName, imgName);
            phantomCmd.append(articleBean.getPhantomjsPath())
                    .append(" ")
                    .append(articleBean.getD2ImgJsPath())
                    .append(" http://localhost:").append(port).append(contextPath).append(tmpPath)
                    .append(" ")
                    .append(" \"")
                    .append(TableUtils.strTrans(tblContent, true))
                    .append("\" ")
                    .append(target);
        }else {
            target = winPath;
            dest = String.format("%s%s\\%s\\%s\\%s", articleBean.getArticleLocalPath(), date, tmpName, "tmp", imgName);
            phantomCmd.append(articleBean.getPhantomjsPath())
                    .append(" ")
                    .append(articleBean.getD2ImgJsPath())
                    .append(" http://localhost:").append(port).append(contextPath).append(tmpPath)
                    .append(" \"")
                    .append(TableUtils.strTrans(tblContent, false))
                    .append("\" ")
                    .append(target);
        }

        Process process = TableUtils.executeTbl(phantomCmd.toString());
        String info = ExecuteUtils.getInputInfo(process);
        logger.info("phantomjs log = {}",info);
        if (null != process) {
            try {
                process.waitFor();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //wkhtml将html转成image
        StringBuilder wkCmd = new StringBuilder();
        if (osName.contains("linux")) {
         
            wkCmd.append("wkhtmltoimage --encoding utf8 ").append(target).append(" ").append(dest);
        }else {
            wkCmd.append("wkhtmltoimage ").append(target).append(" ").append(dest);
        }
        Process p = TableUtils.executeTbl(wkCmd.toString());
        if (null != p) {
            try {
                p.waitFor();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        logger.info("[" + traceId + "]" + "{}渲染完毕", tblName);
        PageContent tbl = new pageContent();
        tbl.setTraceId(traceId);
        tbl.setContentKey(tblName);
        String img = ImageUtils.toBase64Str(dest);
        //替换base64头
        if (img.contains(Constants.BASE64_HEADER_JPG)) {
            img = img.substring(img.indexOf(Constants.BASE64_HEADER_JPG) + Constants.BASE64_HEADER_JPG.length());
        } else if (img.contains(Constants.BASE64_HEADER_PNG)) {
            img = img.substring(img.indexOf(Constants.BASE64_HEADER_PNG) + Constants.BASE64_HEADER_PNG.length());
        }
        //压缩图片
        ImageUtils.compress(dest);
        
        //img 标签图片src
        String contentValue = "\"\"/";
        List pageContents = pageContentService.selectByTraceAndKey(tbl);
        if (null != pageContents && pageContents.size() > 0) {
            tbl = pageContents.get(0);
            tbl.setIsDel(Constants.IS_DEL_NORMAL);
            tbl.setOutime(new Date());
            tbl.setContentValue(contentValue);   
            //落库
            pageContentService.updateSelective(tbl);
        } else {
            tbl.setContentValue(contentValue);
            tbl.setContentType(Constants.TYPE_TBL); 
            pageContentService.insert(tbl);
        }


    }

}



import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.BASE64Decoder;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;


/**
 * @author Hongyi Zheng
 * @date 2018/7/30
 */
public class TableUtils {

    private static final Logger logger = LoggerFactory.getLogger(TableUtils.class);

    public static Process executeTbl(String cmd){

        logger.info("命令行执行:"+cmd);

        Runtime rt = Runtime.getRuntime();
        try {
            return rt.exec(cmd);
        } catch (IOException e) {
            logger.error("phantomjs IO异常,cmd = {}", cmd);
            return null;
        }


    }

    /**
     * 命令行字符串转义
     * @param str 初始字符串
     * @return
     */
    public static String strTrans(String str,boolean isLinux){
        if (isLinux) {
            return str.replaceAll("<","\\<");
        }else {
            return str.replace("\"", "\\\"");
        }
    }

}


import net.coobird.thumbnailator.Thumbnails;
import sun.misc.BASE64Encoder;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.DecimalFormat;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

public class ImageUtils {

    //压缩图片
    public static void compress(String path){
        try {
            Thumbnails.of(path).scale(1).outputQuality(1).toFile(path);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static String toBase64Str(String imgPath){
        InputStream in = null;
        byte[] data = null;
        // 读取图片字节数组
        try {
            in = new FileInputStream(imgPath);
            data = new byte[in.available()];
            in.read(data);
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 对字节数组Base64编码
        BASE64Encoder encoder = new BASE64Encoder();
        // 返回Base64编码过的字节数组字符串
        return encoder.encode(data);
    }

}



phantomJS脚本如下:

//phantomJS脚本
var page = require('webpage').create(),
    system = require('system'),
    url,
    tblContent,
    dest;

if (system.args.length === 0) {
    console.log('phantom : 参数错误!');
    phantom.exit();
} else if (system.args.length === 1) {
    console.log('phantom : url未指定!');
    phantom.exit();
} else {
    start = Date.now();
    url = system.args[1];
    tblContent = system.args[2];
    dest = system.args[3];
    var fs = require("fs");

    //输出访问webpage页面的log
    page.onConsoleMessage = function (msg) {
        console.log(msg);
    };

    //viewportSize being the actual size of the headless browser
    page.viewportSize = {width: 1024, height: 768};

    page.open(url, function (status) {
        if (status === 'success') {
            //页面打开成功则调用appendDiv()函数
            var tbl = page.evaluate(function(tblContent){
                appendDiv(tblContent);
                return document.getElementById('table').getBoundingClientRect();
            },tblContent);
            console.log('phantom : 页面加载成功,用时:' + (Date.now() - start) + 'ms');

            try {
                fs.write(dest, page.content, 'w');
                console.log('phantom : 本地文件写入成功' + dest);
            } catch (e) {
                console.error(e);
            }

        } else {
            console.log('phantom : 页面加载失败,status:' + status);
        }
        //exit phantomJs in 1 secs
        setTimeout(function () {
            phantom.exit();
        }, 1000);
    });
}

3.phantomjs访问html模板




    
    
    
    Document
    
    
    


4.主要使用的工具:

phantomjs:http://phantomjs.org/

基于QtWebKit内核的无头浏览器,可以完成模拟浏览器行为,后台操作页面。常用于dom操作,CSS选择器,web测试,爬虫 

可选的图片渲染工具wkhtml2image/html2canvas

5.在html页面使用html2canvas对表格渲染成图片后,需要使用ajax回调后端接口,注意可能导致跨域问题




    
    
    
    Document
    
    
    
    
    


好了,这样就完成了把html表格标签转为图片保存。

 

你可能感兴趣的:(Java)