1. 程式人生 > >使用phantomjs無界瀏覽器截圖

使用phantomjs無界瀏覽器截圖

一、前言:問題的背景

我之前一直負責一個公眾號的專案,老大在別的公眾號看到一個功能,可以把自己的盈利記錄做一個好看的H5截圖分享出去,介面是可以分享出去的,但是如果分享的是一個H5的話,有很多人都懶得點進去,但是如果生成圖片的話,把圖片分享出去就更加直觀也更加吸引人。現在在新媒體盛行的時代,這個應用場景還是很多的,因此今天來介紹一下如何實現。可以關注一下金匯微策略的公眾號的最新策略的功能體驗,因為我們的公眾號只有分析師有這個許可權操作。

二、使用的技術:

  • phantomjs 官網連結
    這個後面我會說windows下和linux下的安裝的一些小的區別
  • freemarker
  • bat檔案或者.sh檔案

現在可用的技術:
- html2canvas.js js的實現方案
但是截圖不清晰,同是如果是跨域的圖片的話,會無法截圖
- html2image.jar java的生成方案
實話說,這個jar包的依賴我找了一早上,結果放棄了。同時據網上介紹介面如果複雜的話生成的效果也是不理想
- DJNativeSwing-SWT.jar java方案
這種方案是可以行的,親自試過的,但是可以從上面看到我用的不是這個方案,因為這種採用的是swing的無界瀏覽器實現的,但是這個執行緒難以結束,同時無法對動態的頁面生成,所以這種方案也被否決掉了
- phantomjs js的實現方案
內建webkit,使用無界瀏覽器的方式截圖,基本上能規避上述的所有的缺點。下面著重介紹這種方式

三、流程:

  • 首先我們依據ftl模板,用freemarker來生成html檔案,採用這個步驟的原因是因為,可以生成動態的頁面的截圖
  • 生成bat檔案來執行phantomjs命令,網上很多沒有這一步,但是我發現依據網上找到的方案,結果都是無法生成截圖的,因為採用絕對路徑的方法執行命令是黑白的,所以這裡我變通了一下使用批處理檔案或者sh指令碼來實現
  • java Process 執行bat或者sh檔案來執行phantomjs命令來進行截圖,這裡有個坑,①:生成的截圖沒有文字,②:文字非常的模糊

    • 這裡我也得出一個經驗,要找準問題的根源,文字的問題肯定是系統的字型檔案有問題,我們的測試伺服器為windows server的,我在本地沒有問題但是測試伺服器有問題,同時正式的伺服器linux也是有問題的,
  • 把生成的圖上傳到素材然後傳送客服訊息供使用者分享

3.1、所需要的檔案:
  • phantomjs.js
  • page.ftl
  • gener.bat|gener.sh

四、程式碼

4.1、使用freemarker生成html介面
/**
     * 依據ftl模板來生成html,並返回html的名字和(因為不同的人生成的html的資料是不一致的)
     * 並且返回生成的html的名字
     *
     * @return 返回生成的每個人的html
     */
    public static Map<String,String> generHtml(AnalystShare share) throws IOException, TemplateException, InterruptedException {
        Configuration ftlCfg = new Configuration();
        ftlCfg.setEncoding(Locale.getDefault(),"utf-8");
        ftlCfg.setDirectoryForTemplateLoading(new File(Const.FETCH_SCREEN_DIR));
        Template template = ftlCfg.getTemplate(Const.TEMPLATE_NAME,"utf-8");

        String generHtmlName = System.currentTimeMillis()+".html";
        String generHtmlPath = Const.FETCH_SCREEN_DIR + generHtmlName;
        File file = new File(generHtmlPath);
        if (!file.exists()) {
            file.createNewFile();
        }
        OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(file),"utf-8");
        PrintWriter printWriter = new PrintWriter(writer);
        Map map = new HashMap<>();
        map.put("share", getFormatShare(share));
        template.setEncoding("utf-8");
        template.process(map, printWriter);
        Thread.sleep(2000);
        printWriter.close();
        Map pathMap = new HashMap<String,String>();
        pathMap.put("generHtmlName",generHtmlName);
        pathMap.put("generHtmlPath",generHtmlPath);
        return pathMap;
    }

上面有很多點需要注意:
- ①:亂碼的問題
- ②:把生成的html的名字和全路徑返回,在執行phantomjs命令需要html的名字,同時我們生成HTML在完成截圖後還是需要刪除這個檔案的,不然伺服器會越積累越多
- ③:休眠的原因是freemarker生成html的時候需要時間來執行,然後關閉位元組流,這個可以依據自己的伺服器來進行調節

4.2、生成bat檔案或者sh檔案
/**
     * 建立bat檔案並把bat檔案的全路徑以及img的全路徑全部返回回去
     * @param generHtmlName
     * @return
  */
    public static Map<String, String> generBat(String generHtmlName) throws IOException {
        String batFileName = System.currentTimeMillis()+".bat";
        String batFilePath = Const.FETCH_SCREEN_DIR + batFileName;
        String generImgName = System.currentTimeMillis()+".jpg";
        String generImgPath = Const.FETCH_SCREEN_DIR +generImgName;
        File file = new File(batFilePath);
        FileWriter writer = new FileWriter(file);
        writer.write("e:");
        writer.write(System.getProperty("line.separator"));
        writer.write("cd " + Const.FETCH_SCREEN_DIR );
        writer.write(System.getProperty("line.separator"));
        writer.write("phantomjs.exe phantomjs.js " + generHtmlName + " " + generImgName);
        writer.flush();
        writer.close();
        Map map = new HashMap<String, String>();
        map.put("generBatPath", batFilePath);
        map.put("generImgPath", generImgPath);
        map.put("generImgName",generImgName);
        return map;
    }

上面是生成bat的,sh的命令如下,由於篇幅的原因不在寫sh的部分,直接把下面的命令寫入到sh檔案即可。注意*.html 這個地方要依據你程式生成的HTML的名字為準,上面我是採用毫秒值進行命名的,依據自己的需求進行修改即可,cd的路徑也是需要修改的,這裡我把所有的檔案放在一個目錄下。一定要修改成自己實際的檔案的名字和目錄,下面是我進行本地測試的時候的檔案。
image.png

cd /data/wwwroot/default/upload/screen/
./phantomjs phantomjs.js test.html test.png
4.2、執行bat|sh檔案
 /**
     * 執行bat檔案,生成圖片
     * @param batPath
     */
    public static void execute(String batPath) throws IOException, InterruptedException {
        Runtime rt = Runtime.getRuntime();
        Process exec = rt.exec(batPath);
        Thread.sleep(5000);
        exec.destroyForcibly();
        exec.destroy();
        return;
    }
4.3、做善後工作,刪除生成的檔案
 /***
     * 做完所有的善後的工作,把所有生成的檔案delete掉
     * @param htmlPath
     * @param batPath
     * @param imgPath
     */
    public static void destoryAllGenerFile(String htmlPath, String batPath, String imgPath) {
        File file = new File(htmlPath);
        file.deleteOnExit();
        file = new File(batPath);
        file.deleteOnExit();
        file = new File(imgPath);
        file.deleteOnExit();
    }
4.4、phantomjs.js

執行phantomjs指令碼,生成快照指令碼

/**
 * phantomJs 指令碼
 */
var page = require('webpage').create(), system = require('system'), address, output, size;

if (system.args.length < 3 || system.args.length > 5) {
    phantom.exit(1);
} else {
    address = system.args[1];
    output = system.args[2];
    //定義寬高
   /* page.viewportSize = {
        width : 1024,
        height : 768
    };*/
    page.open(address, function(status) {
        var bb = page.evaluate(function() {
            return document.getElementsByTagName('html')[0].getBoundingClientRect();
        });
        page.clipRect = {
            top : bb.top,
            left : bb.left,
            width : bb.width,
            height : bb.height
        };
        window.setTimeout(function() {
            page.render(output);
            page.close();
            console.log('渲染成功...');
            console.log(address);
        }, 1000);
    });
}

ps:由於ftl檔案設計到公司的業務,所以不公開出來,依據這個流程大體是可以生成的。下面說下過程中遇到的問題。
- 亂碼的問題,上面已經介紹過了,
- 生成圖片沒有字,在網上搜linux安裝字型,把windows上字型複製到linux上面然後執行相關的命令進行安裝。如果有問題可以留言。