【翻译】轻松实现 Killer 级应用 Snap

今天在博客园看了一篇文章,说是自己实现了 Snap 的功能,我去试用了一下感觉还不错。看了看后面的留言,并且搜索了一些网上的资料,在CodeProject发现一篇还不错的文章,翻译出来给大家看看。How to take screenshot (thumbnail) of a web site with ASP.NET 2.0?

获取 windows application 的截图很容易,但是获取一个网页的截图往往让人感觉摸不着头脑,不知道如何下手。在 windows application 中我们可以使用 .Net framework 2.0 的 WebBrowser 组件来获取我们需要的网站,然后使用 .DrawToBitmap 函数去获取当前网站的 bitmap。这个组件在 windows application 中工作良好,那他在 Web site 中的表现如何呢?能不能实现我们想要的功能呢?

不要忘记 Asp.net 和windows application 都是在 .net framework 上运行的。这就使我们可以把在 Windows application 实现方法用在 asp.net application 上面。我的初步想法是初始化一个隐藏的 WebBrowser 组件,并且在其中加载我们的 web site 以便参生截图。当然我们还是会面对前面我们提到的 asp.net application 和 web forms application 的问题。

使用 WebBrowser 组件

Dim MyBrowser As New WebBrowser
MyBrowser.ScrollBarsEnabled = False
MyBrowser.Size = New Size(1027, 768)
MyBrowser.Navigate(”http://www.deveload.com”)
Dim myBitmap As New Bitmap(1024, 768)
Dim DrawRect As New Rectangle(0, 0, 1024, 768)
MyBrowser.DrawToBitmap(myBitmap, DrawRect)

在我们定义自己的 WebBrowser 变量之后,我们会给他设置一些属性如把 ScrollBarsEnabled 设置成 false 等。我们调用 navigate 方法去打开我的网站 “www.pinzui.cn”。接下来我们定义了 BitMap 变量, 绘图区域(DrawRect)并且调用 DrawToBitmap 方法获取我们的截图。
看起来仿佛一切都是正常的,但是当你试图运行程序的时候,你会获得 too many errors。第一个问题就是我们不能等待我们想要截图的网站被 WebBrowser 组件完全加载就去获取截图。其实我们只是获取了一个网站加载中的实例的截图。通常情况下我们需要等待想要被截图的网站完全加载才能去获取截图。

MyBrowser.Navigate(Me.URL)
While MyBrowser.ReadyState <> WebBrowserReadyState.Complete
Application.DoEvents()
End While

在我们网站开始加载的时候,我们只是做哑循环去等待我们想要截图的网站被完全加载。为什么我们要试用 Application.DoEvents()呢,这是个技巧,我晚会给大家解释。下面是我们遇到的下一个问题,我们使用的 WebBrowser 空间实际上是一个 COM 对象。我们需要在一个单独的线程里自由的使用 WebBrowser 控件。我们所有的获取截图的程序应该被控制运行在一个独立的线程里面。


Dim NewTh As New Threading.Thread(AddressOf DoIt)
NewTh.SetApartmentState(Threading.ApartmentState.STA)
NewTh.Start()

让我们回到前面那个等待 WebBrowser 控件的循环里。重新思考一下。我们使用一个单独的线程在不停的 loop 等待我们想要截图的网站完全加载。通常情况下这个循环会使我们的程序死锁,直到网页加载完循环结束才把控制权重新交付给 WebBrowser。但是为什么我们的程序没有被死锁呢,对了,Application.DoEvents()就是答案。这个方法会在循环过程中把控制权交付给我们的 WebBrowser 控件。

创建网页截图


Dim imgOutput As System.Drawing.Image = myBitmap
Dim oThumbNail As System.Drawing.Image = New Bitmap(twidth, theight, & _
imgOutput.PixelFormat)
Dim g As Graphics = Graphics.FromImage(oThumbNail)
g.CompositingQuality = Drawing2D.CompositingQuality.HighSpeed
g.SmoothingMode = Drawing2D.SmoothingMode.HighSpeed
g.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBilinear
Dim oRectangle As Rectangle = New Rectangle(0, 0, twidth, theight)
g.DrawImage(imgOutput, oRectangle

在上面的代码中,我们定义了 imgOutput 并且把名为 myBitmap 的原始图像付给他。twidth,theight  是表示我们目标截图大小的变量。我们通过具有目标大小的哑图像创建了一个 grapics 对象。我们为 graphics 对象设置了一下哦属性,并且调用了 DrawImage 方法来产生截图。

最终的类

Imports System
Imports System.Drawing
Imports System.Drawing.Imaging
Imports System.Windows.Forms
Imports System.Diagnostics
Namespace GetSiteThumbnail
Public Class GetImage
Private S_Height As Integer
Private S_Width As Integer
Private F_Height As Integer
Private F_Width As Integer
Private MyURL As String
Property ScreenHeight() As Integer
Get
Return S_Height
End Get
Set(ByVal value As Integer)
S_Height = value
End Set
End Property
Property ScreenWidth() As Integer
Get
Return S_Width
End Get
Set(ByVal value As Integer)
S_Width = value
End Set
End Property
Property ImageHeight() As Integer
Get
Return F_Height
End Get
Set(ByVal value As Integer)
F_Height = value
End Set
End Property
Property ImageWidth() As Integer
Get
Return F_Width
End Get
Set(ByVal value As Integer)
F_Width = value
End Set
End Property
Property WebSite() As String
Get
Return MyURL
End Get
Set(ByVal value As String)
MyURL = value
End Set
End Property
Sub New(ByVal WebSite As String, ByVal ScreenWidth As Integer, &_
ByVal ScreenHeight As Integer, ByVal ImageWidth As Integer,&_
ByVal ImageHeight As Integer)
Me.WebSite = WebSite
Me.ScreenWidth = ScreenWidth
Me.ScreenHeight = ScreenHeight
Me.ImageHeight = ImageHeight
Me.ImageWidth = ImageWidth
End Sub
Function GetBitmap() As Bitmap
Dim Shot As New WebPageBitmap(Me.WebSite, Me.ScreenWidth, &_
Me.ScreenHeight)
Shot.GetIt()
Dim Pic As Bitmap = Shot.DrawBitmap(Me.ImageHeight, &_
Me.ImageWidth)
Return Pic
End Function
End Class
Class WebPageBitmap
Dim MyBrowser As WebBrowser
Dim URL As String
Dim Height As Integer
Dim Width As Integer
Sub New(ByVal url As String, ByVal width As Integer, &_
ByVal height As Integer)
Me.Height = height
Me.Width = width
Me.URL = url
MyBrowser = New WebBrowser
MyBrowser.ScrollBarsEnabled = False
MyBrowser.Size = New Size(Me.Width, Me.Height)
End Sub
Sub GetIt()
MyBrowser.Navigate(Me.URL)
While MyBrowser.ReadyState <> WebBrowserReadyState.Complete
Application.DoEvents()
End While
End Sub
Function DrawBitmap(ByVal theight As Integer, &_
ByVal twidth As Integer) As Bitmap
Dim myBitmap As New Bitmap(Width, Height)
Dim DrawRect As New Rectangle(0, 0, Width, Height)
MyBrowser.DrawToBitmap(myBitmap, DrawRect)
Dim imgOutput As System.Drawing.Image = myBitmap
Dim oThumbNail As System.Drawing.Image = New Bitmap(twidth, &_
theight, imgOutput.PixelFormat)
Dim g As Graphics = Graphics.FromImage(oThumbNail)
g.CompositingQuality = Drawing2D.CompositingQuality.HighSpeed
g.SmoothingMode = Drawing2D.SmoothingMode.HighSpeed
g.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBilinear
Dim oRectangle As Rectangle = New Rectangle(0, 0, twidth, theight)
g.DrawImage(imgOutput, oRectangle)
Try
Return oThumbNail
Catch ex As Exception
Finally
imgOutput.Dispose()
imgOutput = Nothing
MyBrowser.Dispose()
MyBrowser = Nothing
End Try
End Function
End Class
End Namespace
 

如何使用代码?


Partial Class _Default
Inherits System.Web.UI.Page
Protected Sub Button1_Click(ByVal sender As Object, &_
ByVal e As System.EventArgs) Handles Button1.Click
Dim NewTh As New Threading.Thread(AddressOf DoIt)
NewTh.SetApartmentState(Threading.ApartmentState.STA)
NewTh.Start()
End Sub
Sub DoIT()
Try
Dim thumb As New GetSiteThumbnail.GetImage("http://www.deveload.com/",&_
1024, 768, 320, 240)
Dim x As System.Drawing.Bitmap = thumb.GetBitmap()
x.Save("C:\Inetpub\wwwroot\screeny\deveload.jpg")
Catch ex As Exception
Dim y As System.IO.StreamWriter = System.IO.File.CreateText("C:\Inetpub\wwwroot\screeny\error.txt")
y.WriteLine(ex.Message & vbCrLf & ex.Source)
y.Flush()
y.Close()
End Try
End Sub
End Class

上面的代码会创建一个我们写的类的实例并且获取网页截图。如果有什么错误发生了,他会创建一个文本文件,并且把错误写到里面。但是千万不要忘记 DoIT 类会运行在一个独立的线程里面。

Conclusion(结论)

这个系统可以完美工作在90%以上的网站。但是那些通过 AJAX 或者 Flash 在网页加载完成后在调用外部数据的网站,这个系统就不太管用了。

PS:我把它改写成C#的,调试了好长时间都没有通过。原文也没有提供源码。在 Web application 里面好像是不能引用System.Windows.Form。困惑了一个晚上了,今天把翻译好的文章放出来,各位大侠看看,能不能给出个解决方案或者源码之类的。

posted on 2006-12-25 09:27  JesseZhao  阅读(3812)  评论(20编辑  收藏  举报

导航