使用PhantomJS在服務器端渲染並生成Echarts圖片

一,使用的jar包

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-exec</artifactId>
    <version>1.3</version>
</dependency>
<dependency>
    <groupId>com.github.abel533</groupId>
    <artifactId>ECharts</artifactId>
    <version>3.0.0.2</version>
</dependency>
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
</dependency>
<!-- Open API在線文檔 -->
<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-ui</artifactId>
    <version>1.3.4</version>
</dependency>
<!-- selenium測試工具使用的phantomjs驅動 -->
<dependency>
    <groupId>com.codeborne</groupId>
    <artifactId>phantomjsdriver</artifactId>
    <version>1.4.4</version>
</dependency>

二,項目目錄結構

使用PhantomJS在服務器端渲染並生成Echarts圖片

三,建立要用的js(echarts-screenshot.js)

function Base64() {
    // private property
    _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    // public method for encoding
    this.encode = function (input) {
        var output = "";
        var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
        var i = 0;
        input = _utf8_encode(input);
        while (i < input.length) {
            chr1 = input.charCodeAt(i++);
            chr2 = input.charCodeAt(i++);
            chr3 = input.charCodeAt(i++);
            enc1 = chr1 >> 2;
            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
            enc4 = chr3 & 63;
            if (isNaN(chr2)) {
                enc3 = enc4 = 64;
            } else if (isNaN(chr3)) {
                enc4 = 64;
            }
            output = output +
            _keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
            _keyStr.charAt(enc3) + _keyStr.charAt(enc4);
        }
        return output;
    }

    // public method for decoding
    this.decode = function (input) {
        var output = "";
        var chr1, chr2, chr3;
        var enc1, enc2, enc3, enc4;
        var i = 0;
        input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
        while (i < input.length) {
            enc1 = _keyStr.indexOf(input.charAt(i++));
            enc2 = _keyStr.indexOf(input.charAt(i++));
            enc3 = _keyStr.indexOf(input.charAt(i++));
            enc4 = _keyStr.indexOf(input.charAt(i++));
            chr1 = (enc1 << 2) | (enc2 >> 4);
            chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
            chr3 = ((enc3 & 3) << 6) | enc4;
            output = output + String.fromCharCode(chr1);
            if (enc3 != 64) {
                output = output + String.fromCharCode(chr2);
            }
            if (enc4 != 64) {
                output = output + String.fromCharCode(chr3);
            }
        }
        output = _utf8_decode(output);
        return output;
    }

    // private method for UTF-8 encoding
    _utf8_encode = function (string) {
        string = string.replace(/\r\n/g,"\n");
        var utftext = "";
        for (var n = 0; n < string.length; n++) {
            var c = string.charCodeAt(n);
            if (c < 128) {
                utftext += String.fromCharCode(c);
            } else if((c > 127) && (c < 2048)) {
                utftext += String.fromCharCode((c >> 6) | 192);
                utftext += String.fromCharCode((c & 63) | 128);
            } else {
                utftext += String.fromCharCode((c >> 12) | 224);
                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                utftext += String.fromCharCode((c & 63) | 128);
            }
        }
        return utftext;
    }

    // private method for UTF-8 decoding
    _utf8_decode = function (utftext) {
        var string = "";
        var i = 0;
        var c = c1 = c2 = 0;
        while ( i < utftext.length ) {
            c = utftext.charCodeAt(i);
            if (c < 128) {
                string += String.fromCharCode(c);
                i++;
            } else if((c > 191) && (c < 224)) {
                c2 = utftext.charCodeAt(i+1);
                string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
                i += 2;
            } else {
                c2 = utftext.charCodeAt(i+1);
                c3 = utftext.charCodeAt(i+2);
                string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                i += 3;
            }
        }
        return string;
    }
}

// /////////////////////////////////
system = require('system'); // 獲取參數
//傳遞過來的數據
var base64JsonStr = system.args[1];
//截屏文件保存路徑
var screenshotPath = system.args[2];
// 將base64解密
var paramsJsonStr =  new Base64().decode(base64JsonStr) ;
// 轉換爲json對象
var jsonObj = JSON.parse(paramsJsonStr);
//Echarts的Option參數
var option = jsonObj.option ;
//截圖屏幕的寬高值
var clipWidth = jsonObj.clipWidth ;
var clipHeight = jsonObj.clipHeight ;

function Convert(params){
    this.params = params ;
    this.external = {
        JQUERY3 :  'jquery-1.9.0rc1.js',
        ECHARTS3 : 'echarts3.8.4.min.js'
    }; 
    this.page = require('webpage').create(); // 客戶端
    this.page.viewportSize = { width: clipWidth, height: clipHeight };
    // 日誌監聽console,防止有些內部方法控制檯不顯示
    this.page.onConsoleMessage = function(msg, lineNum, sourceId) {
        console.log('CONSOLE: ' + msg + ' (from line #' + lineNum + ' in "' + sourceId + '")');
    };
    var instance = this ;
    this.page.onError = function(msg, trace) {
        var msgStack = ['PHANTOM ERROR: ' + msg];
        if (trace && trace.length) {
             msgStack.push('TRACE:');
             trace.forEach(function(t) {
               msgStack.push(' -> ' + (t.file || t.sourceURL) + ': ' + t.line + (t.function ? ' (in function ' + t.function +')' : ''));
             });
         }
        console.error(msgStack.join('\n'));
        //失敗,返回錯誤信息
        instance.output("", false, msg); 
    };
    this.page.onLoadStarted = function() { 
        console.log('Start loading...'); 
    }; 
    this.page.onLoadFinished = function (status) { 
        console.log('Loading finished.'); 
    }; 
    // 加載資源監聽
    this.page.onResourceReceived = function (res) {
        console.log('Response (#' + res.id + ', stage =' + res.stage + ', code = ' + res.status + ', redirect= ' + res.redirectURL + ') ');
    };
    console.log("實例化完成");
}

Convert.prototype.init = function(){
    var instance = this ;
    instance.page.open("about:blank", function(status) {        
        // 把指定的外部JS文件注入到當前環境
        var hasJquery = instance.page.injectJs(instance.external.JQUERY3);
        var hasEchart = instance.page.injectJs(instance.external.ECHARTS3);
        // 檢查js是否引用成功
        if (!hasJquery || !hasEchart) {
            instance.output("Could not found " + instance.external.JQUERY3 + " or " + instance.external.ECHARTS3, false);
        }
        // 第一個爲交互執行的方法; 第二個爲傳遞給函數的參數
        instance.page.evaluate(instance.createEchartsDom,instance.params);
        // 定義剪切範圍,若是定義則截取全屏
        instance.page.clipRect = {
            top : 0,
            left : 0,
            width : clipWidth,
            height : clipHeight
        };
        // 渲染
        var result = instance.render();
        // 成功輸出,返回圖片或其餘信息
        instance.output(result, true);
    });
}

Convert.prototype.render = function(){
    var instance = this ;
    switch (instance.params.type) {
    case 'file':
        instance.page.render(screenshotPath);
        return screenshotPath;
    case 'base64':
    default:
        var base64 = instance.page.renderBase64('PNG');
        return base64;
    }
}

Convert.prototype.output = function(content, success, msg) {
    var instance = this ;
    console.log(success ? "[SUCCESS]:" : "[ERROR]:" + content);
    instance.page.close();
    instance.exit(instance.params);
};

Convert.prototype.exit = function (params) {
    console.log("退出phantom") ;
    phantom.exit()
};

//
 // 建立eCharts Dom層
 // @param params 參數
 // params.opt
 // params.width
 // params.height
 // params.outfile
 // params.type = 'PNG'
 //
Convert.prototype.createEchartsDom = function(params) {
    var instance = this ;
    var options =  params.option ;
    // 動態加載js,獲取options數據
    $('<script>')
        .attr('type', 'text/javascript') 
        .appendTo(document.head);
    // 取消動畫,不然生成圖片過快,會出現無數據
    if (options !== undefined) {
        options.animation = false;
    }
    // body背景設置爲白色
    $(document.body).css('backgroundColor', 'white');
    // echarts容器
    var container = $("<div>")
        .attr('id', 'container')
        .css({
            width : params.clipWidth,
            height : params.clipHeight
        }).appendTo(document.body);

    var eChart = echarts.init(container[0]);
    eChart.setOption(options);
} ;

// 構建,入口
new Convert(jsonObj).init();

四,Java代碼

1:截屏操做接口

/**
 * 進行屏幕的截屏操做
 */
public interface ScreenshotProvider {
    /**
     * 根據參數判斷是否支持
     */
    boolean support(ScreenshotParam param);

    /**
     * 執行屏幕裁剪
     */
    ScreenshotResult clip(ScreenshotParam param) throws Exception;
}

2:ScreenshotParam

public class ScreenshotParam {
    /**
     * 訪問的url地址;格式如:
     * 1:about:blank  <br />
     * 2:file///:/path/to/demo.html <br />
     * 3:https://www.baidu.com <br />
     * */
    private String url ;
    // 剪裁寬度
    private Integer clipWidth = 500 ;
    // 剪裁高度 
    private Integer clipHeight = 400 ;
    }

3:ScreenshotResult

public class ScreenshotResult {

    // 截圖保存路徑
    private String screenshotPath;

    public ScreenshotResult() {
    }

    public ScreenshotResult(ScreenshotResult result) {
        if(result == null) {
            return ;
        }
        this.screenshotPath = result.getScreenshotPath() ;
    }

    public ScreenshotResult(String screenshotPath) {
        this.screenshotPath = screenshotPath;
    }
}

4:ScreenshotManager

@Component
public class ScreenshotManager {

    @Autowired
    public List<ScreenshotProvider> providers = new ArrayList<ScreenshotProvider>() ;

    /**
     * 執行屏幕裁剪
     * @throws Exception 
     * */
    public ScreenshotResult clip(ScreenshotParam param) throws Exception {
        for(ScreenshotProvider provider:providers) {
            if(!provider.support(param)) {
                continue ;
            }
            return provider.clip(param) ;
        }
        return null ;
    }
}

5:PhantomjsScreenshotProvider

/**
 * 經過Phantomjs來執行JavaScript文件 <br />
 * github.com/ariya/phantomjs/tree/master/examples
 */
public abstract class PhantomjsScreenshotProvider implements ScreenshotProvider {
    private Logger log = LoggerFactory.getLogger(PhantomjsScreenshotProvider.class);

    protected static String BLANK = " ";

    @Value("${screenshotDir:}")
    protected String screenshotDir;
    @Value("${phantomjsPath:}")
    protected String phantomjsPath;
    // rasterize.js
    protected String jsFilePath;

    @PostConstruct
    public void init() {
        if (StringUtils.isBlank(screenshotDir)) {
            try {
                File classPathDir = ResourceUtils.getFile(ResourceUtils.CLASSPATH_URL_PREFIX);
                File dir = new File(classPathDir.getAbsolutePath() + "/screenshot/phantomjs/echarts/images");
                if (!dir.exists() || !dir.isDirectory()) {
                    dir.mkdirs();
                }
                screenshotDir = dir.getAbsolutePath();
                log.info("screenshotDir:{}", screenshotDir);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }

        if (StringUtils.isBlank(phantomjsPath)) {
            try {
                File exeFile = null;
                if (OSUtil.isLinux() || OSUtil.isMacOSX()) {
                    exeFile = ResourceUtils
                            .getFile(ResourceUtils.CLASSPATH_URL_PREFIX + "drivers/phantomjs/v2.1.1/linux64/phantomjs");
                } else {
                    exeFile = ResourceUtils.getFile(
                            ResourceUtils.CLASSPATH_URL_PREFIX + "drivers/phantomjs/v2.1.1/window/phantomjs.exe");
                }
                phantomjsPath = exeFile.getAbsolutePath();
                log.info("phantomjsPath:{}", phantomjsPath);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
        if (StringUtils.isBlank(jsFilePath)) {
            try {
                // 執行的JS文件路徑
                File jsFile = ResourceUtils.getFile("classpath:static/js/rasterize.js");
                // 生成的圖片名稱
                jsFilePath = jsFile.getAbsolutePath();
                log.info("jsFilePath:{}", jsFilePath);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    // 生成圖片的所存放路徑
    protected String getRandomImagePath() {
        String outPath = screenshotDir + File.separator + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date())
                + "-" + new Random().nextInt() + ".png";
        return outPath;
    }

    // 執行cmd命令
    protected String commandLine(String url, String screenshotPath) {
        return phantomjsPath + BLANK + jsFilePath + BLANK + url + BLANK + screenshotPath;
    }

    protected void exeCommandLine(String commandLine) throws IOException {
        // Java中使用Runtime和Process類運行外部程序
        Process process = Runtime.getRuntime().exec(commandLine);
        InputStream inputStream = process.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        String tmp = "";
        StringBuffer sb = new StringBuffer();
        while ((tmp = reader.readLine()) != null) {
            sb.append(tmp);
        }
        close(process, reader);
    }

    // 關閉命令
    protected void close(Process process, BufferedReader bufferedReader) throws IOException {
        if (bufferedReader != null) {
            bufferedReader.close();
        }
        if (process != null) {
            process.destroy();
            process = null;
        }
    }

    public String getScreenshotDir() {
        return screenshotDir;
    }

    public void setScreenshotDir(String screenshotDir) {
        this.screenshotDir = screenshotDir;
    }

    public String getPhantomjsPath() {
        return phantomjsPath;
    }

    public void setPhantomjsPath(String phantomjsPath) {
        this.phantomjsPath = phantomjsPath;
    }

    public String getJsFilePath() {
        return jsFilePath;
    }

    public void setJsFilePath(String jsFilePath) {
        this.jsFilePath = jsFilePath;
    }
}

6: EchartsScreenshotProvider

@Component
public class EchartsScreenshotProvider extends PhantomjsScreenshotProvider {

    private Logger log = LoggerFactory.getLogger(EchartsScreenshotProvider.class);

    @PostConstruct
    public void init() {
        if (StringUtils.isBlank(jsFilePath)) {
            try {
                // 執行的JS文件路徑
                File jsFile = ResourceUtils.getFile("classpath:static/js/echarts-screenshot.js");
                // 生成的圖片名稱
                jsFilePath = jsFile.getAbsolutePath();
                log.info("jsFilePath:{}", jsFilePath);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
        super.init();
    }

    @Override
    public boolean support(ScreenshotParam param) {
        return param instanceof EchartsScreenshotParam;
    }

    @Override
    public ScreenshotResult clip(ScreenshotParam param) throws Exception {
        EchartsScreenshotParam echartsScreenshotParam = (EchartsScreenshotParam) param;
        if (echartsScreenshotParam == null || echartsScreenshotParam.getOption() == null) {
            throw new Exception("參數不能爲空") ; 
        }
        if(StringUtils.isBlank(jsFilePath)) {
            throw new Exception("執行的js文件不能爲空") ;    
        }
        //屏幕截圖的保存位置
        String screenshotPath = getRandomImagePath();

        // 實例化
        Gson gson = new Gson();
        // 將map轉成JSON
        String paramsJsonStr = gson.toJson(echartsScreenshotParam);
        Encoder encoder = Base64.getEncoder();
        // 轉換爲base64編碼是爲了防止執行指令的時候出問題
        String base64Str = encoder.encodeToString(paramsJsonStr.getBytes("UTF-8"));

        String[] args = new String[3];
        args[0] = jsFilePath;
        args[1] = base64Str;
        args[2] = screenshotPath;
        // 執行腳本
        try {
            CommandExecUtil.exec(phantomjsPath, args, null) ;
        } catch (Exception e) {
            throw e;
        }
        return new ScreenshotResult(screenshotPath);
    }

}

7:EchartsScreenshotParam

/**
 * 使用Echarts的數據來渲染出圖片並截取
 */
public class EchartsScreenshotParam extends ScreenshotParam {

    // echart的Option
    public Option option;

    public String type = "file";

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public Option getOption() {
        return option;
    }

    public void setOption(Option option) {
        this.option = option;
    }
}

8, CommandExecUtil

public class CommandExecUtil {

    /**
     * @param commandLine
     *            須要執行的命令行,例如:"AcroRd32.exe /p /h c://file.pdf"
     */
    public static int exec(String commandLine) throws Exception {
        CommandLine cmdLine = CommandLine.parse(commandLine);
        DefaultExecutor executor = new DefaultExecutor();
        int exitValue = executor.execute(cmdLine);
        return exitValue;
    }

    /**
     * @param command
     *            命令,例如:AcroRd32.exe
     * @param args
     *            參數,例如:/C , /p, /h
     */
    public static int exec(String command, String[] args, Map<String, ?> substitutionMap) throws Exception {
        CommandLine cmdLine = new CommandLine(command);
        if (args != null && args.length > 0) {
            for (String arg : args) {
                cmdLine.addArgument(arg);
            }
        }
        if (substitutionMap != null) {
            cmdLine.setSubstitutionMap(substitutionMap);
        }
        DefaultExecutor executor = new DefaultExecutor();       
        // executor.setExitValue(1);
        // ExecuteWatchdog watchdog = new ExecuteWatchdog(60000);
        // executor.setWatchdog(watchdog);
        int exitValue = executor.execute(cmdLine);

        return exitValue;
    }
}

9: Controller中測試

@Tag(name = "屏幕截圖", description = "截屏測試控制器")
@RestController
@RequestMapping(path = "/screenshot")
public class ScreenshotController {

    @Autowired
    private ScreenshotManager manager;

    @Operation(summary = "使用Freemarker生成html並截圖", description = "", tags = { "屏幕截圖" })
    @GetMapping(path = "/freemarker")
    public ScreenshotResult freemarkerScreenshot(String templateName) throws Exception {
        FreemarkerSeleniumScreenshotParam param = new FreemarkerSeleniumScreenshotParam(DriverTypeEnum.PhantomJS);
        param.setTemplatePath(templateName);
        param.setClipWidth(600);
        param.setClipHeight(500);
        return manager.clip(param);
    }

    @Operation(summary = "使用Selenium截圖", description = "", tags = { "屏幕截圖" })
    @GetMapping(path = "/selenium/phantomjs")
    public ScreenshotResult seleniumScreenshot(String url) throws Exception {
        SeleniumScreenshotParam param = new SeleniumScreenshotParam(DriverTypeEnum.PhantomJS);
        param.setUrl(url);
        return manager.clip(param);
    }

    @Operation(summary = "使用phantomjs進行echarts截圖", description = "", tags = { "屏幕截圖" })
    @GetMapping(path = "/phantomjs/echarts")
    public ScreenshotResult phantomjsEchartsScreenshot() throws Exception {
        EchartsScreenshotParam param = new EchartsScreenshotParam();
        param.setOption(getEchartOption());

        return manager.clip(param);
    }

    public Option getEchartOption() {
        Option option = new Option();
        option.legend("this is the legend");
        option.toolbox().show(true).feature(Tool.mark, Tool.dataView, new MagicType(Magic.line, Magic.bar),
                Tool.restore, Tool.saveAsImage);
        option.calculable(true);
        option.tooltip().trigger(Trigger.axis).formatter("Temperature : <br/>{b}km : {c}°C");

        ValueAxis valueAxis = new ValueAxis();
        valueAxis.axisLabel().formatter("{value} °C");
        option.xAxis(valueAxis);

        CategoryAxis categoryAxis = new CategoryAxis();
        categoryAxis.axisLine().onZero(false);
        categoryAxis.axisLabel().formatter("{value} km");
        categoryAxis.boundaryGap(false);
        categoryAxis.data(0, 10, 20, 30, 40, 50, 60, 70, 80);
        option.yAxis(categoryAxis);

        Line line = new Line();
        line.smooth(true).name("line ssss").data(15, -50, -56.5, -46.5, -22.1, -2.5, -27.7, -55.7, -76.5).itemStyle()
                .normal().lineStyle().shadowColor("rgba(0,0,0,0.4)");
        option.series(line);
        return option;
    }

    public ScreenshotManager getManager() {
        return manager;
    }

    public void setManager(ScreenshotManager manager) {
        this.manager = manager;
    }
}

以上是經過命令行的方式啓動phantomjs並執行JS文件後將Echarts圖片截取下來的過程;除了這種方式外,還能夠經過selenium來配合完成,例如如下代碼:javascript

SeleniumPhantomjsScreenshotProvider

/**
 * Some of the examples of Headless Drivers include <br />
 * 1,HtmlUnit <br />
 * 2,Ghost <br />
 * 3,PhantomJS <br />
 * 4,ZombieJS <br />
 * 5,Watir-webdriver <br />
 * */
@Component
public class SeleniumPhantomjsScreenshotProvider implements ScreenshotProvider {

    private static final Logger log = LoggerFactory.getLogger(SeleniumPhantomjsScreenshotProvider.class);

    @Value("${screenshotDir:}")
    protected String screenshotDir;
    @Value("${phantomjsPath:}")
    protected String phantomjsPath;

    @PostConstruct
    public void init() {
        if (StringUtils.isBlank(screenshotDir)) {
            try {
                File classPathDir = ResourceUtils.getFile(ResourceUtils.CLASSPATH_URL_PREFIX);
                File dir = new File(classPathDir.getAbsolutePath() + "/screenshot/selenium/phantomjs/images");
                if (!dir.exists() || !dir.isDirectory()) {
                    dir.mkdirs();
                }
                screenshotDir = dir.getAbsolutePath();
                log.info("screenshotDir:{}", screenshotDir);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }

        if (StringUtils.isBlank(phantomjsPath)) {
            try {
                File exeFile = null;
                if (OSUtil.isLinux() || OSUtil.isMacOSX()) {
                    exeFile = ResourceUtils
                            .getFile(ResourceUtils.CLASSPATH_URL_PREFIX + "drivers/phantomjs/v2.1.1/linux64/phantomjs");
                } else {
                    exeFile = ResourceUtils.getFile(
                            ResourceUtils.CLASSPATH_URL_PREFIX + "drivers/phantomjs/v2.1.1/window/phantomjs.exe");
                }
                phantomjsPath = exeFile.getAbsolutePath();
                log.info("phantomjsPath:{}", phantomjsPath);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    // 生成圖片的所存放路徑
    protected String getRandomImagePath() {
        String outPath = screenshotDir + File.separator + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date())
                + "-" + new Random().nextInt() + ".png";
        return outPath;
    }

    @Override
    public boolean support(ScreenshotParam param) {
        if(!(param instanceof SeleniumScreenshotParam)) {
            return false ;
        }
        SeleniumScreenshotParam params = (SeleniumScreenshotParam)param ;
        return params.getDriverType() == DriverTypeEnum.PhantomJS ;
    }

    @Override
    public ScreenshotResult clip(ScreenshotParam param) throws Exception {
        return new ScreenshotResult(getPicByPhantomjs(param));
    }

    private String getPicByPhantomjs(ScreenshotParam param) throws Exception {
        log.warn("使用phantomjs截圖連接:{}", param.getUrl());
        // 定義圖片存儲路徑
        DesiredCapabilities capabilities = new DesiredCapabilities(BrowserType.PHANTOMJS, "", Platform.ANY);
        WebDriver driver = null;
        try {
            // ssl證書支持
            capabilities.setCapability(CapabilityType.ACCEPT_SSL_CERTS, true);
            // 截屏支持
            capabilities.setCapability(CapabilityType.TAKES_SCREENSHOT, true);
            // css搜索支持
            capabilities.setCapability(CapabilityType.SUPPORTS_FINDING_BY_CSS, true);
            // js支持
            capabilities.setJavascriptEnabled(true);
            capabilities.setCapability(CapabilityType.SUPPORTS_LOCATION_CONTEXT, true);
            capabilities.setCapability(CapabilityType.SUPPORTS_NETWORK_CONNECTION, true);

            // defines whether to execute the script in the page or not (defaults to true).
            capabilities.setCapability("phantomjs.page.settings.javascriptEnabled", true);
            // defines whether to load the inlined images or not (defaults to true)
            capabilities.setCapability("phantomjs.page.settings.loadImages", true);
            // defines whether local resource (e.g. from file) can access remote URLs or not
            // (defaults to false).
            capabilities.setCapability("phantomjs.page.settings.localToRemoteUrlAccessEnabled", true);

            // sets the user name used for HTTP authentication.
            // capabilities.setCapability("phantomjs.page.settings.userName", "");
            // sets the password used for HTTP authentication.
            // capabilities.setCapability("phantomjs.page.settings.password", "");
            // defines whether web security should be enabled or not (defaults to true).
            capabilities.setCapability("phantomjs.page.settings.webSecurityEnabled", true);
            // (in milli-secs) defines the timeout after which any resource requested will
            // stop trying and proceed with other parts of the page.
            // capabilities.setCapability("phantomjs.page.settings.resourceTimeout", 5000);

            capabilities.setCapability("phantomjs.page.settings.userAgent",
                    "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0");
            capabilities.setCapability("phantomjs.page.customHeaders.User-Agent",
                    "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0");

            // 驅動支持(第二參數代表的是你的phantomjs引擎-例如phantomjs.exe所在的路徑)
            capabilities.setCapability(PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY, phantomjsPath);

            // 建立***面瀏覽器對象
            driver = new PhantomJSDriver(capabilities);
            driver.manage().window().setSize(new Dimension(param.getClipWidth(), param.getClipHeight()));
            // 設置隱性等待(做用於全局)
            driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS).pageLoadTimeout(20, TimeUnit.SECONDS);

            // 發起請求
            try {
                driver.get(param.getUrl());
            } catch (Exception e) {
                log.error("開發地址 {} 發生異常:{}", param.getUrl(), e.getMessage());
                throw e;
            }
            try {
                Thread.sleep(4 * 1000);
            } catch (InterruptedException e) {
            }
            // 指定了OutputType.FILE作爲參數傳遞給getScreenshotAs()方法,其含義是將截取的屏幕以文件形式返回。
            File srcFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
            try {
                Thread.sleep(2 * 1000);
            } catch (InterruptedException e) {
            }

            // 利用FileUtils工具類的copyFile()方法保存getScreenshotAs()返回的文件對象
            String outFilePath = this.getRandomImagePath();

            FileUtils.copyFile(srcFile, new File(outFilePath));

            return outFilePath;
        } catch (Exception e) {
            log.error("使用phantomjs截圖時異常:{}", e.getMessage());
            throw e;
        } finally {
            if (driver != null) {
                driver.close();
                driver.quit();
            }
        }
    }

    protected void scrollWindow(JavascriptExecutor js) {
        // 頁面下滑10次,每次下滑加載2s
        for (int i = 0; i < 10; i++) {
            js.executeScript("window.scrollBy(0,1000)"); // 睡眠2s等js加載完成
            try {
                Thread.sleep(2 * 1000);
            } catch (InterruptedException e) {
            }
        }
    }

    protected void clickAction(PhantomJSDriver driver, String elementClassName) {
        if (elementExist(driver, By.className(elementClassName))) {
            WebElement inputBox = driver.findElement(By.className(elementClassName));
            Actions action = new Actions(driver);
            action.click(inputBox).build().perform(); // 元素點擊 後等待加載
            try {
                Thread.sleep(2 * 1000);
            } catch (InterruptedException e) {
            }
        }
    }

    private boolean elementExist(PhantomJSDriver driver, By by) {
        return driver.findElement(by) == null ? false : true;
    }

}

SeleniumScreenshotParam

public class SeleniumScreenshotParam  extends ScreenshotParam{

    //使用的驅動器的類型
    private DriverTypeEnum driverType ;

    public SeleniumScreenshotParam(DriverTypeEnum driverType) {
        this.driverType = driverType ;
    }

    public DriverTypeEnum getDriverType() {
        return driverType;
    }
}

DriverTypeEnum

public enum DriverTypeEnum {

    PhantomJS,HtmlUnit,ZombieJS;
}
相關文章
相關標籤/搜索