前言
前面有写博文,利用phantomjs实现网页快照,分别适配windows,linux,mac,好不容易搞好了,结果把项目镜像放到docker运行容器的时候,居然失效了。docker内部不也是linux内核么,居然会出问题。找了半天,发现是docker中很多库不兼容可能,不能运行phantomjs命令。想了很多办法,比如更换类库,但是已更换会出现连锁反应,其他一个一个都要换,明显不行。很久才解决,在这里记录一下,帮助以后需要的朋友。
开始
由于docker容器中的类库不兼容直接运行phantomjs命令,所以我们选择用docker创建一个phantomjs容器,在项目容器中调用这个phantomjs容器来运行phantomjs相关功能。
1.首先要创建phantomjs容器,我这里运用的是docker-compose启动容器,所以写在了docker-compose.yml文件中,下文列出,有关使用docker-compose创建容器的可以移步【docker】——docker-compose部署springboot镜像项目
docker run -d -p 4444:4444 --shm-size=256m -e TZ=Asia/Shanghai selenium/standalone-chrome
2.phantomjs容器创建好之后使用docker ps命令是这样的
对应4444端口。
那么我们上面所说的在自己的项目中调用 phantomjs驱动,可以直接写phantomjs容器映射的ip:4444,但是这样显然不合适,因为这个docker容器部署在哪里是会变的,所以我们可以使用docker run --link命令来是两个容器之间可以相互通信,我们就可以写成phantomjs:4444的格式了,这样无论docker容器部署在哪里,只要我们在启用项目容器的同时创建一个phantomjs容器就可以了。
下面附上我的docker-compose文件,既可以同时创建所需要的容器,也可以用link命令将其联系起来
version: "3"
services:
phantomjs:
image: selenium/standalone-chrome
container_name: phantomjs
privileged: true
ports:
- "4444:4444"
volumes:
- /opt/phantomjs/config:/opt/config/
shm_size: 256m
store:
image: registry.xxx.aliyuncs.com/xxx
container_name: store
restart: always
privileged: true
ports:
- "9527:80"
volumes:
- /opt/store/config:/opt/config/
links:
- phantomjs:phantomjs
这样我们就可以在需要的项目中获取到phantomjs驱动啦!
3.项目代码
下面的代码都是在前面的博文【PhantomJs】——利用phantomjs实现网页快照的两种方式的基础上添加的,如有需要可自便
在之前的博文中有写过判断操作系统的工具类,这里我们新增一个判断是否是docker环境的方法:
public static boolean isDocker() {
if (isInDocker != null) {
return isInDocker;
}
try {
final File dockerinit = new File("/.dockerinit");
if (dockerinit.exists()) {
return isInDocker = true;
}
} catch (Exception ignore) {
}
try {
final File dockerenv = new File("/.dockerenv");
if (dockerenv.exists()) {
return isInDocker = true;
}
} catch (Exception ignore) {
}
return isInDocker = false;
}
之后在phantomjsUtils中新增docker环境是获取网页快照缩略图的方法
private static String getBase64ForDocker(String url, int w, int h) throws MalformedURLException {
RemoteWebDriver driver = new RemoteWebDriver(
new URL("http://phantomjs:4444/wd/hub"),
DesiredCapabilities.chrome());
driver.get(url);
driver.manage().window().setSize(new Dimension(1900,900));
//设置页面加载超时
driver.manage().timeouts().pageLoadTimeout(20, TimeUnit.SECONDS);
//设置查询组件等待时间
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
File file = driver.getScreenshotAs(OutputType.FILE);
driver.quit();
return ImageUtils.thumbnailImage(file, w, h, scale);
}
快照缩略图工具类
原本的博文中并未对快照进行缩略,这里顺便附上图片缩略的工具类
public class ImageUtils {
public static final Boolean DEFAULT_FORCE = false;
private static final String DOT = ".";
/**
* <p>Title: thumbnailImage</p>
* <p>Description: 依据图片路径生成缩略图 </p>
* @param imgFile 图片
* @param w 缩略图宽
* @param h 缩略图高
* @param force 是否强制依照宽高生成缩略图(假设为false,则生成最佳比例缩略图)
*/
public static String thumbnailImage(File imgFile, int w, int h, boolean force){
if(imgFile.exists()){
try {
// ImageIO 支持的图片类型 : [BMP, bmp, jpg, JPG, wbmp, jpeg, png, PNG, JPEG, WBMP, GIF, gif]
String types = Arrays.toString(ImageIO.getReaderFormatNames());
String suffix = null;
// 获取图片后缀
if(imgFile.getName().contains(DOT)) {
suffix = imgFile.getName().substring(imgFile.getName().lastIndexOf(".") + 1);
}// 类型和图片后缀所有小写,然后推断后缀是否合法
if(suffix == null || !types.toLowerCase().contains(suffix.toLowerCase())){
Logger.error("Sorry, the image suffix is illegal. the standard image suffix is {}." + types);
return null;
}
Logger.debug("target image's size, width:{}, height:{}.",w,h);
Image img = ImageIO.read(imgFile);
if(!force){
// 依据原图与要求的缩略图比例,找到最合适的缩略图比例
int width = img.getWidth(null);
int height = img.getHeight(null);
if((width*1.0)/w < (height*1.0)/h){
if(width > w){
h = Integer.parseInt(new java.text.DecimalFormat("0").format(height * w/(width*1.0)));
Logger.debug("change image's height, width:{}, height:{}.",w,h);
}
} else {
if(height > h){
w = Integer.parseInt(new java.text.DecimalFormat("0").format(width * h/(height*1.0)));
Logger.debug("change image's width, width:{}, height:{}.",w,h);
}
}
}
BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics g = bi.getGraphics();
g.drawImage(img, 0, 0, w, h, Color.LIGHT_GRAY, null);
g.dispose();
// 将图片保存在原文件夹并加上前缀
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(bi, "jpg", baos);
byte[] bytes = baos.toByteArray();
String base64 = org.apache.commons.codec.binary.Base64.encodeBase64String(bytes).trim();
base64 = base64.replaceAll("\n", "").replaceAll("\r", "");
return "data:image/png;base64," + base64;
} catch (IOException e) {
Logger.error("generate thumbnail image failed.",e);
}
}else{
Logger.warn("the image is not exist.");
}
return null;
}
}