【翻译】轻松实现 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 @ 2006-12-25 09:27 JesseZhao 阅读(2739) 评论(20)  编辑 收藏 网摘 所属分类: C Asp.net

  回复  引用  查看    
#1楼 2006-12-25 09:41 | 第一控制.NET      
给你们推荐这个。

http://www.cnbeta.com/modules.php?name=News&file=article&mode=flat&sid=19795

效果不错已经开源。.net1.1下的。

不好意思,我只看了下效果。没看他的代码。误导各位了。
  回复  引用  查看    
#2楼 2006-12-25 10:27 | Cat Chen      
为什么大家都认为这是killer app呢……killer app应该是任何人看到都想用的,但普通连HTML模板都不知道如何修改的人肯定不会用Snap.com。

其实很多东西做小规模的和做大规模的不同,简单的方案只能做个demo,好像Snap.com那样被非常多顶级站点所采用的就需要复杂的设计方案以支撑其负载。
  回复  引用  查看    
#3楼 2006-12-25 10:39 | kwklover      
有些时候还是很有价值的.

但很多时候也没什么用处.

比如cnblogs上用了之后.我反而觉得麻烦.等哪个图象出来.我已经进到页面了.... 感觉有碍视觉......

但用在搜索引擎的结果里,我觉得很好.可以先看下snap图,然后决定是否点进去看看,感觉蛮好的...
  回复  引用    
#4楼 2006-12-25 11:41 | ivan[匿名] [未注册用户]
这个可以防止被骗进某一个网站.先看看截图再进去.^_^
  回复  引用    
#5楼 2006-12-25 11:46 | kevinyu[匿名] [未注册用户]
我试着用C#改写了一下,"最终的类"放在另一个项目里,在Web项目里引用就可以了.
这个程序是可以截图,但需要完善的地方太多太多啦,粗略使用了一下,
1,很多网站是截取不到的,截到的为空白
2,如果要截取的网页有弹出窗口,竟然还能够弹出来,如新浪的背投广告.
^_^
  回复  引用    
#6楼 2006-12-25 12:03 | 小游戏[匿名] [未注册用户]
谢谢哦
  回复  引用  查看    
#7楼 2006-12-25 12:32 | Jeffrey Zhao      
应该可以移植成C#的,不知道您遇到什么错误。
  回复  引用  查看    
#8楼 [楼主]2006-12-25 12:42 | 贫嘴老赵      
我转换成功了,中间除了一点错误,都改正了。今天在找一个把vb.net的code转换成c#code的工具,发现都是收费的,比较郁闷
  回复  引用  查看    
#9楼 [楼主]2006-12-25 12:50 | 贫嘴老赵      
再看 otagSnap 这个开源的截图的源代码,等回头看完了自己 diy 一个出来。
  回复  引用    
#10楼 2006-12-25 14:45 | lone [未注册用户]
otagSnap???????
先看下CnBeta的评论再下`````
呵呵,一骗子,就是判断下有无网页截图文件存在,不存在直接提示创建失败
还开源,弄格假货也能开源呐
  回复  引用  查看    
#11楼 2006-12-25 14:55 | edison1024      
实现这种网页截图玩意其实真的是很简单,除了译文中中这种方式,用remoting或者COM+技术都可以实现,把截图功能做成一个服务,在asp.net中调用就可以了。

哎。。。。。。。。。
  回复  引用  查看    
#12楼 [楼主]2006-12-25 15:11 | 贫嘴老赵      
我仔细看了一下otagsnap,就算她的截图不是假的,那么她的开源也是假的。
我看了她的截图的那部分是放到一个线程里面来做的
process.StartInfo.FileName = Server.MapPath("/")+"\\bin\\otagSnapr";
otagSnapr这个东西源码里面没有,我看了一下是个win32的程序,她是处理图片的一个线程,具体结果我就不知道了。
  回复  引用  查看    
#13楼 2006-12-25 17:09 | U2U      
好玩
  回复  引用  查看    
#14楼 2006-12-25 19:08 | Kai.Ma[匿名]      
动作很快喔。

我看了下,不过和我那http://www.365rss.cn/WebPreview.aspx用的方法有点出入,当然,肯定要用单一的线程调用winform控件。

看了大家的回复,看来结果一样有我那样的问题,很多网站会抓到空白。

还不知道是否有解决方法。
  回复  引用  查看    
#15楼 [楼主]2006-12-25 21:21 | 贫嘴老赵      
  回复  引用  查看    
#16楼 2006-12-25 22:42 | AlphaWu      
  回复  引用  查看    
#17楼 2006-12-28 16:29 | 阿蒙[匿名]      
WebBrowser的运行效率很低的,而且很占资源
每次生成切图CPU占用都高得可怕
  回复  引用  查看    
#18楼 2007-02-01 08:45 | a11s.net      
JS之类得东西WBrowser也会搞得.这样一旦出现新的漏洞就很可怕.而且恶意JS之类得也难以避免.它可以让你去 XX网站 运行 XXJS 做XX事情.你的服务器跟肉鸡还有什么区别?包括恶意投票之类也难以避免.

最终解决办法还是需要一个 html解析模块=>BMP 这个模块可以定制比IE更丰富得内容.(就是再做一个专用IE)
  回复  引用    
#19楼 2008-06-24 10:25 | liuhaizhi [未注册用户]
楼主速加我QQ或者邮箱 我有个急的问题问你关于截取图片的,需要改进下
我现在是要做这样一个功能 我有一个DIV 里面我上传两帐图片 图片可以随意拖

动(已实现)
当这两张图片叠合的时候,我点击按钮实现截取这个DIV为一张图片

  回复  引用    
#20楼 2008-06-24 10:33 | liuhaizhi [未注册用户]

标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2006-12-25 09:29 编辑过
"五向定位"职业成长路线公开课(上海、南京、大连)
Google站内搜索


相关链接: