由于公司要求做一个接口,请求这个接口进行网页截图并发送邮件的功能,本来前期是用python写好了,but似乎不太符合要求,那么就用java来重新写一遍,虽然我是java菜鸡,但这并不影响 我修修补补,ctrl c 和ctrl v嘛。
主要问题
此需求很简单,主要就是对网页的截图。 如果网页是自带了 浏览器的滚动条,那么 selenium 一条命令就可以解决整个网页的截图了,然鹅我们这网页是个坑,他的滚动条都是放在div这种元素中内嵌了, 因此需要对 div元素计算高度, 进行滚动截图, 然后将滚动出来的截图拼接到一起。
如图,这玩意分为了好几部分
左侧功能栏(截图时忽略)
顶部功能栏(不可能每滚动一次,就把顶部也截图进去,所以我选择 先截图顶部,然后截图 网页滚动条主体内容)
网页主体滚动条(当然啦, 这个滚动条属于那个非常惹人厌恶的div元素)
可以很清楚的明白,就是这玩意。 因此我们获取浏览器或者窗口高度是没有效果的, 必须先要获取这个div 滚动条的高度, just 像这样:
document.getElementsByClassName('scroll-canvas scroll-canvas--dashboard')[0].scrollHeight
下面展开说
private void getGrafana(String username, String password, String url,
String mailUser, String mailPassword, String[] mailReceiver, String mailServer) {
WebDriver driver=null;
// 定义图片名称
Date d = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String file_name = "sendmail_" + sdf.format(d) + ".png";
try {
//各位如果自己写, 此处直接调用chrome driver就行。我这里是调用了封装的方法
driver = WebDriverUtil.getH5Driver();
driver.get(url);
driver.manage().timeouts().implicitlyWait(20,TimeUnit.SECONDS);
System.out.println("打开网址后等待加载");
// 用户名
By loc = By.xpath("//input[@id='username']");
WebElement ele1 = WebDriverUtil.find_element(driver, loc, 1,10);
ele1.click();
ele1.clear();
ele1.sendKeys(username);
//密码
loc = By.xpath("//input[@id='password']");
WebElement ele2 = WebDriverUtil.find_element(driver, loc, 1, 10);
ele2.click();
ele2.clear();
ele2.sendKeys(password);
//点击登录
loc = By.xpath("//input[@name='submit']");
WebElement ele3 = WebDriverUtil.find_element(driver, loc, 1, 10);
ele3.click();
Thread.sleep(8000);
if(driver.getTitle() != null && driver.getTitle().length() >0){
System.out.println("打开了:" +driver.getTitle());
}else{
System.out.println("没有打开页面,请检查!");
}
try {
long scroll_width = (long) ((JavascriptExecutor)driver).executeScript("return document.body.parentNode.scrollWidth;");
long scroll_height = (long) ((JavascriptExecutor)driver).executeScript("return document.body.offsetHeight;");
//System.out.println(scroll_width + scroll_height);
driver.manage().window().setSize(new Dimension((int)scroll_width, (int) scroll_height));
String js_scroll = "document.getElementsByClassName('scroll-canvas scroll-canvas--dashboard')[0].scrollTop =";
int content_height = (int)(long) ((JavascriptExecutor)driver).executeScript("return document.getElementsByClassName('scroll-canvas scroll-canvas--dashboard')[0].scrollHeight");
int title_height = (int)(long) ((JavascriptExecutor)driver).executeScript("return document.getElementsByClassName('navbar')[0].offsetHeight");
System.out.println("content height is: " + content_height + ", title height is: " + title_height);
WebElement scroll_content = driver.findElement(By.xpath("//div[@class='scroll-canvas scroll-canvas--dashboard']"));
WebElement scroll_top = driver.findElement(By.xpath("//div[@class='navbar']"));
Thread.sleep(2000);
boolean flag = GrafanaSendScreenShot.fullpage_screenshot(driver, file_name, title_height, content_height, js_scroll, scroll_top, scroll_content);
}catch (Exception e){
e.printStackTrace();
System.out.println("计算浏览器高度宽度或者截图失败,请检查");
System.out.println(e.getMessage());
Reporter.log(e.getMessage());
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("加载网页出现问题,请检查");
System.out.println(e.getMessage());
Reporter.log(e.getMessage());
} finally {
if(driver != null) {
driver.close();
driver.quit();
}
}
File file = new File("");
String baseDir = file.getAbsolutePath();
String file_path = baseDir+"/screen/";
File filetmps = new File(file_path+file_name);
boolean flag = filetmps.exists();
if (flag) {
System.out.println("使用截图文件: "+ filetmps.getName());
try{
System.out.println("开始发送邮件...");
GrafanaSendScreenShot.send_email(filetmps, mailUser, mailPassword, mailReceiver, mailServer);
System.out.println("邮件发送成功...");
}catch (Exception e){
e.printStackTrace();
System.out.println(e.getMessage());
Reporter.log(e.getMessage());
}
}else{
System.out.println("截图不存在,请检查或者重试!");
}
}
这里要注意下:
方式1:
JavascriptExecutor jsExecutor = (JavascriptExecutor) driver;
jsExecutor.executeScript("js代码");
方式2:
(JavascriptExecutor)driver).executeScript("js代码");
我作为一个菜鸡,我真看不出来这有啥不同。采用方式1时, 我返回的一直就是null, 简直null到我怀疑人生。方式2是可行的。不知道 大佬们是个啥情况,如果了解麻烦告知我下。
下面是 截图主要代码:
public class GrafanaSendScreenShot {
public static boolean fullpage_screenshot(WebDriver driver, String file_pic, int title_height,
int content_height, String js_scroll, WebElement scroll_top, WebElement scroll_content) throws InterruptedException, IOException {
System.out.println("Starting chrome full page screenshot workaround ...");
// 获取浏览器滚动条高度,宽度等信息
if (content_height == 0 ){
content_height = (int)(long) ((JavascriptExecutor)driver).executeScript("return document.body.parentNode.scrollHeight");
}
int total_width = (int)(long) ((JavascriptExecutor)driver).executeScript("return document.body.offsetWidth");
int total_height = content_height + title_height;
int viewport_width =(int)(long) ((JavascriptExecutor)driver).executeScript("return document.body.clientWidth");
int viewport_height = (int)(long) ((JavascriptExecutor)driver).executeScript("return window.innerHeight");
System.out.println("Total: ("+total_width+ ","+total_height+")" + " Viewport: ("+viewport_width+","+viewport_height+")");
List<List<Integer>> rectangles = new ArrayList<>();
int i = 0;
int count = 0;
int top_height;
int top_width;
while(i<total_height){
int ii = 0;
if(count == 0){
top_height = i + title_height;
}else{
top_height = i + viewport_height;
}
if(top_height > total_height){
top_height = total_height;
}
while (ii < total_width){
top_width = ii + viewport_width;
if (top_width > total_width){
top_width = total_width;
}
System.out.println("Appending rectangle: "+ii+" ,"+ i+" ,"+ top_width+" ,"+ top_height);
List<Integer> tmp_data = new ArrayList<>();
tmp_data.add(ii);
tmp_data.add(i);
tmp_data.add(top_width);
tmp_data.add(top_height);
rectangles.add(tmp_data);
ii = ii + viewport_width;
}
if(count == 0){
i = i + title_height;
}else{
i = i + viewport_height;
}
count = count +1;
}
//搞一张全新的图片撒~
BufferedImage stitched_image = null;
stitched_image = new BufferedImage(total_width, total_height, BufferedImage.TYPE_INT_RGB);
List<Integer> previous = new ArrayList<>();
int part = 0;
int [] offset ;
File file = new File("");
//使用相对路径,将中间过程的截图文件和 最终的文件都放在screen目录下
String baseDir = file.getAbsolutePath();
String file_path = baseDir+"/screen/";
System.out.println("pic tmp file path: " +file_path);
//截图截完一次, 翻滚一次
for(List<Integer> rectangle:rectangles){
if( previous.size()!=0 ){
((JavascriptExecutor)driver).executeScript(js_scroll+"("+ rectangle.get(0) +","+ rectangle.get(1) +" )");
System.out.println("Scrolled To" +"("+ rectangle.get(0) +","+ rectangle.get(1) +" )");
Thread.sleep(1000);
}
String file_name = "part_"+part+".png";
System.out.println("Capturing..."+file_name);
File filetmps = new File(file_path+file_name);
//如果part==0,截图顶部功能栏,这里其实 都是对 scroll这个元素进行截图
if(part == 0){
//指定了OutputType.FILE做为参数传递给getScreenshotAs()方法,其含义是将截取的屏幕以文件形式返回。
File srcFile = scroll_top.getScreenshotAs(OutputType.FILE);
//利用FileUtils工具类的copyFile()方法保存getScreenshotAs()返回的文件对象。
FileUtils.copyFile(srcFile,filetmps);
String jq1 = "$('.navbar').remove()";
String jq2 = "$('.scroll-canvas--dashboard').css('height','100%')";
((JavascriptExecutor)driver).executeScript(jq1);
System.out.println("取消顶部栏显示");
((JavascriptExecutor)driver).executeScript(jq2);
System.out.println("取消底部高度栏显示");
Thread.sleep(1000);
}else{
File srcFile = scroll_content.getScreenshotAs(OutputType.FILE);
FileUtils.copyFile(srcFile,filetmps);
Thread.sleep(1000);
}
Image screenshot = ImageIO.read(new File(file_path+file_name));
if (rectangle.get(1) + viewport_height > total_height) {
offset = new int[]{rectangle.get(0), (total_height - viewport_height)};
}else{
offset = new int[]{rectangle.get(0), rectangle.get(1)};
}
System.out.println("Adding to stitched image with offset " +offset[0]+","+offset[1]);
// 将图片贴到 原始图上
Graphics g =stitched_image.getGraphics();
g.drawImage(screenshot,offset[0],offset[1],null);
g.dispose();
boolean flag = filetmps.exists();
if (flag) {
System.out.println("tmp图片存在,删除");
filetmps.delete();
}else{
System.out.println("tmp图片不存在");
}
part = part + 1;
previous = rectangle;
}
// 最终生成的图片
ImageIO.write(stitched_image,"png", new File(file_path +file_pic));
System.out.println("Finishing chrome full page screenshot workaround...");
return true;
}
//这个是发送邮件啦,简单
public static void send_email(File file_name, String mailUser, String mailPassword, String[] mailReceiver, String mailServer)
throws UnsupportedEncodingException, MessagingException {
Date d = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
Properties properties = new Properties();
//1.创建邮件对象
properties.put("mail.smtp.host", mailServer);// 指定SMTP服务器
properties.put("mail.smtp.auth", "true"); // 指定是否需要SMTP验证
Session session = Session.getInstance(properties);
MimeMessage message =new MimeMessage(session);
//2.设置发件人
// InternetAddress 的三个参数分别为: 邮箱, 显示的昵称(只用于显示, 没有特别的要求), 昵称的字符集编码
message.setFrom(new InternetAddress(mailUser,mailUser,"UTF-8"));
//3.设置多个收件人
//To收件人 CC 抄送 BCC密送
InternetAddress[] sendTo = new InternetAddress[mailReceiver.length];
for (int i=0; i<mailReceiver.length; i++){
System.out.println("发送到:"+ mailReceiver[i]);
sendTo[i] = new InternetAddress(mailReceiver[i]);
}
message.setRecipients(MimeMessage.RecipientType.TO, sendTo);
//收件人单人方式
//message.setRecipient(MimeMessage.RecipientType.TO,new InternetAddress(mailReceiver,"xin","UTF-8"));
// message.addRecipient(MimeMessage.RecipientType.CC,new InternetAddress("409****1@qq.com","xinyuezi","UTF-8"));
//4.设置主题
message.setSubject("截图" ,"UTF-8");
//message.setContent("Test Content:这是一封测试邮件...","text/html;charset=UTF-8");
//一个Multipart对象包含一个或多个bodypart对象,组成邮件正文
MimeMultipart multipart = new MimeMultipart();
//读取本地图片,将图片数据添加到"节点"
MimeBodyPart image = new MimeBodyPart();
DataHandler dataHandler1 = new DataHandler(new FileDataSource(file_name));
image.setDataHandler(dataHandler1);
image.setContentID("image_screen");
//创建文本节点
MimeBodyPart text = new MimeBodyPart();
text.setContent("<font color=black>截图时间: "+sdf.format(d)+"<br></font><img src='cid:image_screen'/>","text/html;charset=UTF-8");
// 附件, 暂时用不到
//创建附件节点 读取本地文件,并读取附件名称
// MimeBodyPart file1 = new MimeBodyPart();
// DataHandler dataHandler2 = new DataHandler(new FileDataSource("C:\\Users\\Public\\Pictures\\Sample Pictures\\growing.png"));
// file1.setDataHandler(dataHandler2);
// file1.setFileName(MimeUtility.encodeText(dataHandler2.getName()));
//
// MimeBodyPart file2 = new MimeBodyPart();
// DataHandler dataHandler3 = new DataHandler(new FileDataSource("E:\\Tang\\topics.xlsx"));
// file2.setDataHandler(dataHandler3);
// file2.setFileName(MimeUtility.encodeText(dataHandler3.getName()));
//将文本和图片添加到multipart
multipart.addBodyPart(text);
multipart.addBodyPart(image);
multipart.setSubType("mixed");//混合关系
message.setContent(multipart);
message.saveChanges();
Transport transport = session.getTransport("smtp");
transport.connect(mailUser, mailPassword);
transport.sendMessage(message,message.getAllRecipients());
transport.close();
}
}
##引用
1: https://www.cnblogs.com/MasterMonkInTemple/p/9970512.html
2: https://blog.csdn.net/xinyuezitang/article/details/85768462
3: https://www.cnblogs.com/chbyiming-bky/articles/8940105.html
4: https://www.iteye.com/blog/lijingshou-2019323