【PhantomJs】——利用phantomjs实现网页快照的两种方式

前言

今天遇到一个需求,是前端给后台一个网址,后台需要返回这个网址的快照回去,刚接手的时候一脸懵,了解了一下,是项目中有个播放视频或者直播的区域,需要一张图片来作为封面图,但是如果专门去存的话不方便,也不灵活。

既然有需求,那就只能找方法了,网上各种翻阅,方法不多,也不算少,但是很杂,很多工具及代码都有不足之处,有的比较慢,有的是会出现可视化的工具框,比如IFrame,显然不好,最后确定了用phantomjs,也应该是用的最多的。

准备

由于phantomjs是一个工具,所以对于不同的操作系统是不通用的,所以要下载各个系统的工具包来做适配,在phantomjs官网镜像下载phantomjs工具包。

我这里选择了2.1.1的版本。

下载好之后解压并对名字做修改先暂时放在F盘

这里项目用的是springboot项目,其他框架下面内容请自行调整

application.yml

由于phantomjs工具包比较大,四个加起来也有将近200M,放在项目中显然是不合适的,所以我们选择在yml文件中配置路径,将工具包放在服务器本地路径中

phantomJs:
  windows: F:/
  mac: /opt/
  linux: /opt/

这里就是配置了windows,mac,linux环境下phantomjs工具包所需要放置的路径。

ResultBean.java

接口放回结果还是通过的ResultBean类,这里简化只有code和data两个属性:

public class ResultBean<T> {
    private Integer code;
    private T data;

    public Integer getCode() {return code;}

    public void setCode(Integer code) {this.code = code;}

    public T getData() {return data;}

    public void setData(T data) {this.data = data;}

    public ResultBean() {}

    public ResultBean(Integer code, T data) {
        this.code = code;
        this.data = data;
    }

    @Override
    public String toString() {
        return "ResultBean{" +"code='" + code + '\'' +", data=" + data +'}';
    }
}

PlatformUtils.java

这里写一个工具类来区分不同的操作系统,来进行不同系统phantomjs的选择

public class PlatformUtils {
    private static final Logger logger = LoggerFactory.getLogger(PlatformUtils.class);
    private static final String OS_NAME = System.getProperty("os.name").toLowerCase();
    private static final String OS_ARCH = System.getProperty("os.arch").toLowerCase();
    private static final String OSARCH = "64";

    public static boolean isWindows() {
        return OS_NAME.contains("windows");
    }

    public static boolean isMac() {
        return OS_NAME.contains("mac");
    }

    public static boolean isLinux() {
        return OS_NAME.contains("linux");
    }

    public static boolean is64OsArch() {
        return OS_ARCH.contains(OSARCH);
    }
}

下面我们开始这两种方式的演示与实现:

第一种方式

        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>2.45.0</version>
        </dependency>

        <dependency>
            <groupId>com.codeborne</groupId>
            <artifactId>phantomjsdriver</artifactId>
            <version>1.2.1</version>
        </dependency>
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.phantomjs.PhantomJSDriver;
import org.openqa.selenium.phantomjs.PhantomJSDriverService;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.TimeUnit;

/**
 * @description: 快照工具类
 * @author: WangZhiJun
 * @create: 2019-10-25 14:36
 **/
public class SnapShotUtils {
    private static Logger logger = LoggerFactory.getLogger(SnapShotUtils.class);
    private static final String EQUAL = "=";


    public static String getDriverPath(String path) {
        if (PlatformUtils.isWindows()) {
            logger.info("当前系统:Windows");
            return path + "phantomjs/windows/bin/phantomjs.exe";
        }else {
            if (PlatformUtils.isLinux()) {
                logger.info("当前系统:Linux");
                if (PlatformUtils.is64OsArch()) {
                    logger.info("当前系统:64位");
                    return path + "phantomjs/linux-x86_64/bin/phantomjs";
                } else {
                    return path + "phantomjs/linux-i686/bin/phantomjs";
                }
            } else {
                logger.info("当前系统:Mac");
                return path + "phantomjs/macosx/bin/phantomjs";
            }
        }
    }

    public static String getBase64(String url, String path) {
        //设置必要参数
        DesiredCapabilities dcaps = new DesiredCapabilities();
        //ssl证书支持
        dcaps.setCapability("acceptSslCerts", true);
        //截屏支持
        dcaps.setCapability("takesScreenshot", true);
        //css搜索支持
        dcaps.setCapability("cssSelectorsEnabled", true);
        //js支持
        dcaps.setJavascriptEnabled(true);
        //驱动支持(第二参数表明的是你的phantomjs引擎所在的路径)
        dcaps.setCapability(PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY,
                getDriverPath(path));
        //创建无界面浏览器对象
        PhantomJSDriver driver = new PhantomJSDriver(dcaps);

        //设置隐性等待(作用于全局)
        driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS);
        //打开页面
        driver.get(url);
        String base64  = ((TakesScreenshot)driver).getScreenshotAs(OutputType.BASE64);
        logger.info("成功获取URL快照,快照大小:"+imageSize(base64));
        return base64;
    }

    /**
     * 通过图片base64流判断图片等于多少字节
     * image 图片流
     */
    private static Integer imageSize(String image) {
        // 1.需要计算文件流大小,首先把头部的data:image/png;base64,(注意有逗号)去掉。
        String str = image.substring(22);
        //2.找到等号,把等号也去掉
        int equalIndex = str.indexOf("=");
        if (str.indexOf(EQUAL) > 0) {
            str = str.substring(0, equalIndex);
        }
        //3.原来的字符流大小,单位为字节
        int strLength = str.length();
        //4.计算后得到的文件流大小,单位为字节
        return (strLength - (strLength / 8) * 2);
    }

    public static void main(String[] args) {
        System.out.println(getBase64("https://www.baidu.com","F:/"));
    }
}

第二种方式

        <dependency>
            <groupId>net.coobird</groupId>
            <artifactId>thumbnailator</artifactId>
            <version>0.4.8</version>
        </dependency>
import net.coobird.thumbnailator.Thumbnails;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.charset.StandardCharsets;

/**
 * @description: 快照工具类
 * @author: WangZhiJun
 * @create: 2019-10-25 14:36
 **/
public class PhantomUtils {
    private static Logger logger = LoggerFactory.getLogger(PhantomUtils.class);
    private static final Integer BITNUM = 64;
    private static final String BLANK = " ";
    private static final String EQUAL = "=";

    /**
     * 功能描述  关闭命令
     *
     * @param process 1
     * @param bufferedReader 1
     * @author CosmosRay
     * @date 2019/4/13
     */
    public static void close(Process process, BufferedReader bufferedReader) throws IOException {
        if (bufferedReader != null) {
            bufferedReader.close();
        }
        if (process != null) {
            process.destroy();
        }
    }
    /**
     * 功能描述  通过URL获取该URL快照base64
     * @param url 连接
     * @return  java.lang.String
     * @author  CosmosRay
     * @date  2019/4/13
    */
    public static String getBase64(String url, String path) throws IOException {
        if(StringUtils.isBlank(url)){
            return null;
        }
        String[] pathArray;
        if (PlatformUtils.isWindows()) {
            pathArray = getWindowsPath(path);
        } else {
            if (PlatformUtils.isLinux()) {
                if (PlatformUtils.is64OsArch()) {
                    pathArray = getLinuxPath(64, path);
                } else {
                    pathArray = getLinuxPath(32, path);
                }
            } else {
                pathArray = getMacPath(path);
            }
        }
        return printUrlScreen(url, pathArray);
    }

    /**
     * 功能描述 windows系统服务器路径地址
     *
     * @return java.lang.String[]
     * @author CosmosRay
     * @date 2019/4/13
     */
    private static String[] getWindowsPath(String path) {
        String[] pathArray = new String[3];
        //图片临时保存路径
        pathArray[0] = path + "phantomjs/windows";
        //phantomjs主程序路径
        pathArray[1] = path + "phantomjs/windows/bin/phantomjs";
        //phantomjs参数js路径
        pathArray[2] = path + "phantomjs/windows/examples/rasterize.js";

        return pathArray;
    }

    /**
     * 功能描述
     *
     * @return java.lang.String[]
     * @author CosmosRay
     * @date 2019/4/13
     */
    private static String[] getLinuxPath(int osArch, String path) {
        String[] pathArray = new String[3];

        if (osArch == BITNUM) {
            //图片临时保存路径
            pathArray[0] = path + "phantomjs/linux-x86_64";
            //phantomjs主程序路径
            pathArray[1] = path + "phantomjs/linux-x86_64/bin/phantomjs";
            //phantomjs参数js路径
            pathArray[2] = path + "phantomjs/linux-x86_64/examples/rasterize.js";
        } else {
            //图片临时保存路径
            pathArray[0] = path + "phantomjs/linux-i686";
            //phantomjs主程序路径
            pathArray[1] = path + "phantomjs/linux-i686/bin/phantomjs";
            //phantomjs参数js路径
            pathArray[2] = path + "phantomjs/linux-i686/examples/rasterize.js";
        }
        return pathArray;
    }
    /**
     * 功能描述   MAC系统
     * @return  java.lang.String[]
     * @author  CosmosRay
     * @date  2019/4/13
    */
    private static String[] getMacPath(String path) {
        String[] pathArray = new String[3];
        //图片临时保存路径
        pathArray[0] = path + "phantomjs/macosx";
        //phantomjs主程序路径
        pathArray[1] = path + "phantomjs/macosx/bin/phantomjs";
        //phantomjs参数js路径
        pathArray[2] = path + "phantomjs/macosx/examples/rasterize.js";
        return pathArray;
    }

    /**
     * 通过图片base64流判断图片等于多少字节
     * image 图片流
     */
    private static Integer imageSize(String image) {
        // 1.需要计算文件流大小,首先把头部的data:image/png;base64,(注意有逗号)去掉。
        String str = image.substring(22);
        //2.找到等号,把等号也去掉
        int equalIndex = str.indexOf("=");
        if (str.indexOf(EQUAL) > 0) {
            str = str.substring(0, equalIndex);
        }
        //3.原来的字符流大小,单位为字节
        int strLength = str.length();
        //4.计算后得到的文件流大小,单位为字节
        return (strLength - (strLength / 8) * 2);
    }

    /**
     * 功能描述   执行cmd命令
     *
     * @param imagePath 图片名称绝对路径
     * @param url       URL地址
     * @return java.lang.String
     * @author CosmosRay
     * @date 2019/4/13
     */
    private String cmd(String imagePath, String url, String[] pathArray) {
        return pathArray[1] + BLANK + pathArray[2] + BLANK + url + BLANK + imagePath;
    }

    /**
     * 功能描述
     *
     * @param url 连接URL
     * @return void 无
     * @author CosmosRay
     * @date 2019/4/13
     */
    private static String printUrlScreen(String url, String[] pathArray) throws IOException {
        if(StringUtils.isBlank(url)){
            return null;
        }
        //图片路径
        String imagePath = pathArray[0] + "/menu.png";
        //Java中使用Runtime和Process类运行外部程序
        PhantomUtils phantomTools2 = new PhantomUtils();
        Process process = Runtime.getRuntime().exec(phantomTools2.cmd(imagePath, url, pathArray));
        InputStream inputStream = process.getInputStream();

        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
        String rs;
        while ((rs = reader.readLine()) != null) {
            close(process, reader);
        }

        File file;
        BufferedInputStream in;
        byte[] ret = null;
        file = new File(imagePath);
        if (file.exists()) {
            in = new BufferedInputStream(new FileInputStream(file));
            Thumbnails.Builder<? extends InputStream> builder = Thumbnails.of(in).size(256, 256);
            BufferedImage bufferedImage = builder.asBufferedImage();
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ImageIO.write(bufferedImage, "png", baos);
            ret = baos.toByteArray();
        }
        String base64 = null;
        if (ret != null) {
            //转换成base64串
            String pngBase64 = Base64.encodeBase64String(ret).trim();
            //删除 \r\n
            pngBase64 = pngBase64.replaceAll("\n", "").replaceAll("\r", "");
            base64 = "data:image/png;base64," + pngBase64;
            logger.info("成功获取URL快照,快照大小:"+imageSize(base64));
        }
        return base64;
    }

    public static void main(String[] args) throws IOException {
        System.out.println(getBase64("https://baidu.com", "F:/"));
    }
}

演示

@RestController
@RequestMapping()
public class ScreenshotController {

    @Autowired
    private ScreenshotService screenshotService;

    @GetMapping("/snap")
    public ResultBean<String> getScreenshot(@RequestParam(value = "url") String url){
        ResultBean resultBean = new ResultBean();
        try {
            resultBean.setCode(1);
            resultBean.setData(screenshotService.getScreenshot(url));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resultBean;
    }
}
public interface ScreenshotService {
    String getScreenshot(String url) throws Exception;
}
@Service
public class ScreenshotServiceImpl implements ScreenshotService {

    @Value("${phantomJs.mac}")
    private String phantomJsMac;

    @Value("${phantomJs.windows}")
    private String phantomJsWin;

    @Value("${phantomJs.linux}")
    private String phantomJsLinux;

    @Override
    public String getScreenshot(String url) throws IOException {
        String diverPath;
        if (PlatformUtils.isMac()) {
            diverPath = phantomJsMac;
        } else if (PlatformUtils.isWindows()) {
            diverPath = phantomJsWin;
        } else {
            diverPath = phantomJsLinux;
        }
        //第一种
        //return SnapShotUtils.getBase64(url, diverPath);
        //第二种
        return PhantomUtils.getBase64(url, diverPath);
    }
}

结果

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值