1、闲扯
有一个GUI编程的开源框架叫SWT(Standard Widget Toolkit),它与sun公司的AWT、Swing类似。非界面编程人员,这里就不扯它们的优缺点了。
或许很多人都没听说过SWT,但是如果说出由它制作出来的一个工具,大家可能会对它肃然起敬。这个工具就是IBM开发的Eclipse。于是我们如果要给Eclipse开发插件,那么就得对SWT非常了解了。
2、要求
要截取网页形成图片,需要1-2要求。这里的1是要配置java.libarary.path;2是需要两个jar包。
1要求:配置java.library.path。
也许大家都知道配置环境变量path。然而这个环境变量和这个java.library.path有什么作用呢?
path:指定的是classpath,也就是通过这个变量可以找到相应的Java类;而有时候,Java可能需要利用到本地的代码库文件(在Windows上为dll文件,在Linux上为so文件)。系统如何找到这个动态链接库呢?当然也需要一个变量指向,这个变量就是java.library.path。
2需求:拿到两个jar包。
一个jar包是Eclipse当中的。打开Eclipse的安装目录,我的目录为“E:\eclipse\plugins”,在这个下面会有一个org.eclipse.swt.win32.x86_64_***.jar的包。
另一个包就是基于这个Eclipse的包开发的进行实际应用(如:截取网页形成图片、渲染成Html文件)的jar包。这个jar包具体是谁开发的就不知道了。它的项目点击可下载。
3、部署
将两个包加到自己的项目当中。另外,在另外一个地方将Eclipse里面拿出的那个jar进行解压,可以看到里面有三个dll的动态链接库文件。我们将这三个文件拷贝到某个目录下。我的目录为“D:/swt”。 然后,在开发运行的时候可以配置一下虚拟机的参数,方法为:右键点击项目——>Run as——>Run Configurations。如下进行配置,这样就使得java.library.path指向了程序运行过程中所需要的几个动态链接库。要不然会抛出:java.lang.UnsatisfiedLinkError no *****.dll in java.library.path,如此的异常。到这里,我们就可以进行测试了。看看它惊奇的效果。
4、分析
我们在学习GUI编程的时候,都知道,先要进行视图的布局。这里的视图就是一个个的面板。设置面板的样式、大小。面板上面叠加面板做出超炫的样式,或者在面板上添加监听事件与用户进行交互。
这里进行截取网页也是如此。网页怎么被渲染出来的?网页的样式布局是被浏览器根据脚本渲染出来的效果,这里我们就利用了浏览器这个“面板”。在浏览器的下面还有一个面板“shell”。这也是说,shell面板上有一个browser的面板,我们传一个URL给browser面板后,面板能对其返回的页面进行渲染。这里我们执行browser面板的大小就为一帧,现在我们只关心它的高度,这个高度是可以由我们指定的(当然需要修改其源代码),默认为500。我们通过在java代码当中执行javascript脚本,就能返回这时候网页内容的总高度。这个总高度/500+1就是我们需要将这个网页刷屏几次才能得到一个完整的网页。项目的核心方法如下(默认的要刷好几次屏才能将网页捕获完整;我将其改成刷一次屏就能捕获完整的网页,但是要将其线程睡眠时间弄长点,要不然网页还没渲染出来,就已经捕获完了,那么形成的图片的数据就会有所残缺):
1 final int perHight=2027; 2 public void render(final File saveLocation) throws RenderingException 3 { 4 final File localSaveLocation = new File(saveLocation.getParentFile().getAbsolutePath() + File.separator + "localhost"); 5 localSaveLocation.mkdirs(); 6 System.out.println("Saving temporary images in directory :" + localSaveLocation.getAbsolutePath()); 7 8 String widthStr = null; 9 //根据javascript脚本得到网页内容的宽度和高度 10 widthStr = this.getJavascriptValue("document.body.scrollWidth+‘-‘+document.body.scrollHeight"); 11 if (widthStr == null) 12 throw new RenderingException("Could not acquire Document height andf width by javascript."); 13 14 final int width = SCREEN_WIDTH; 15 //如果网页内容高度大于10000那么就取值10000,否则就是内容高度本身 16 int nonFinalHeight = Math.min(10000, Integer.parseInt(widthStr.split("-")[1])); 17 18 //perHight就是每一帧刷多高,如果网页内容高度小于一帧的取值高度就取值一帧的高度;否则,就取值内容的高度 19 final int height = nonFinalHeight < perHight ? perHight: nonFinalHeight; 20 21 System.out.println("Screen sizes :" + width + " ," + height); 22 23 int start=0; 24 25 //然后将作为内容高度的height变量除以一帧高度,也就是要对这个网页进行刷屏多少次。 26 //我们也就要将网页内容进行移动,移动网页内容,只要移动浏览器Y轴上的滚动条就行, 27 //初始状态,不需要滚动,也就是当i=0时。当i=1时,就需要滚动一帧的内容,因为每次滚动,滚动条初始在0位置,因此每次start都是从0开始。 28 29 30 for (int i = 0; i < 1 + (height / perHight); i++) 31 { 32 if (i != 0) 33 { 34 this.execute("window.scrollTo("+start+"," + i * (perHight - scrollbarY) + ");"); 35 try 36 { 37 Thread.sleep(10000);//这个时间稍微长点,要不页面还没有刷出来就已经捕获了这一帧 38 } 39 catch (InterruptedException e) 40 { 41 throw new RenderingException(e); 42 } 43 } 44 final int j = i; 45 //下面的异步方法就是捕获一帧的网页 46 //这里呈现的屏幕的高度和宽度是固定的,只是在这个固定的区域当中,内容在变而已。 47 //因此,我们只要对这一块固定的区域进行捕获就可以了。 48 display.syncExec(new Runnable() 49 { 50 public void run() 51 { 52 if (j == 0 || j < (height / perHight)) 53 capture(localSaveLocation.getAbsolutePath(), j, SCREEN_WIDTH,perHight, 0, 0); 54 else 55 capture(localSaveLocation.getAbsolutePath(), j, SCREEN_WIDTH, (height % perHight == 0 ? perHight : height % perHight), 0, perHight - (height % perHight)); 56 } 57 }); 58 } 59 display.syncExec(new Runnable() 60 { 61 public void run() 62 { 63 try 64 { 65 final Image result = new Image(display, width, height); 66 GC gc = new GC(result); 67 for (int i = 0; i < (1 + height / perHight); i++) 68 { 69 70 ImageLoader imageLoader = new ImageLoader(); 71 File partialImageFile = new File(localSaveLocation, "savedImage" + i + ".jpg"); 72 FileInputStream fis = new FileInputStream(partialImageFile); 73 Image loadedImage = new Image(display, imageLoader.load(fis)[0]); 74 gc.drawImage(loadedImage, 0, i * (perHight - scrollbarY)); 75 loadedImage.dispose(); 76 fis.close(); 77 // Delete the partial image file once we are done with 78 // it : 79 partialImageFile.delete(); 80 } 81 gc.dispose(); 82 ImageLoader imageSaver = new ImageLoader(); 83 imageSaver.data = new ImageData[] { result.getImageData() }; 84 System.out.println("Saving image to location :" + saveLocation); 85 FileOutputStream fos = new FileOutputStream(saveLocation); 86 imageSaver.save(fos, SWT.IMAGE_JPEG); 87 fos.close(); 88 result.dispose(); 89 90 } 91 catch (Exception e) 92 { 93 e.printStackTrace(); 94 } 95 } 96 }); 97 98 }