Makale Özeti

İster başka web sitelerinin olsun ister kendi web sitenizin iç sayfalarının olsun ekran görüntülerini gerçek zamanlı olarak kullanmaya ihtiyacınız varsa artık kendi kodunuzu yazarak bu işlemleri yapabilirsiniz. Bu makalemizde ASP.NET sunucu taraflı programlama ile bir web sitesinin ekran görüntüsünü almayı ve kullanmayı inceliyoruz.

Makale

Web sitelerinin ekran görüntülerini (screenshot) alarak kullanıma sunan hizmetler internet üzerinde mevcut. Bu makalemizde ASP.NET tarafında nasıl başka bir sitenin ekran görüntüsü resim olarak alabileceğimizi inceleyeceğiz. İlk olarak ufak bir noktaya değinmekte fayda var; bahsedeceğimiz teknik bazı özel koşullarda (hedef web sitesinin görsel öğeleri JavaScript ile düzenlemesi) başarılı sonuç vermeyebiliyor. O nedenle eğer yüzde yüzlük bir başarı istiyorsanız ihtiyacınız olan hedef web sitesinin ekran görüntüsü otomatik olarak alan bir windows uygulaması ile web siteniz arasında entegrasyon sağlamak. Peki o zaman neden bu işi web ortamında yapıyoruz? Maalesef normal barındırma (hosting) hizmetlerinde sunucu tarafına kendi uygulamalarınızı yüklemenize izin verilemez. Makalemizde izlediğimiz teknikler ile herhangi bir barındırma alanında bu tarz bir uygulama yapabilirsiniz.

İlk olarak gelin ufak bir demo ile aslında ne yapacağımızı görelim. Aşağıdaki adreste bu makaledeki kodun kullanıldığı bir uygulama var. Uygulama John K. tarafından makalemizdeki kodun üzerine renk palet analizi de eklenerek düzenlendi. Sistem, girilen adresin ekran görüntüsünü aldıktan sonra görüntü içerisinde kullanılan ana renkleri buluyor. Siz de makalemizdeki yöntemi kullanarak eminim ki çok daha farklı uygulamalar yapabilirsiniz. Eğer beni uygulamalarınızla ilgili haberdar edebilirseniz ayrıca sevinirim.

http://www.todotoh.com/rgb/rgbanalysis.aspx

İş Başına

Peki bu işi nasıl yapacağız? İlk olarak .NET Framework'un en önemli konseptini hatırlatmakta fayda var. Web ile windows uygulamalarında aslında biz aynı Framework'ü kullanıyoruz. Yani eğer istersek windows forms uygulamalarında kullandığımız tüm kütüphaneleri ve objeleri web forms uygulamalarımızda da kullanabiliriz. Bir web sitesinin ekran görüntüsü almak için WinForms'da kullanabileceğimiz WebBrowser adında bir objemiz var.

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

Yukarıdaki kodumuz içerisinde yeni bir WebBrowser objesi yarattıktan sonra ekranın kenarlarında kaydırma çubuklarının gözükmemesi için ScrollBarsEnabled özelliğini False olarak düzenliyoruz. Bir sonraki adımda Browser (Tarayıcı) objemizin boyutunu 1024*768 yaparak sanki web sitesi o çözünürlükte bir ekranda açılmışmış gibi bir ortam yaratıyoruz. WebBrowser objesine ait .Navigate metoduna verdiğimiz parametre ile sitemizin yüklenmesini sağlıyoruz. Uygun resim ve çizim alanı değişkenlerimizi de tanımladıktan sonra yine WebBrowser objesine ait .DrawToBitmap metodunu kullanarak görüntüyü değişkenimize alıyoruz.

Tabi olay bu kadar kolay değil. Elimizdeki ilk sorun web sitesinin yüklenmesini beklemek zorunda olmamız. Bunun için kör bir döngü kullanacağız.

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

Kodumuzda WebBrowser objesinin ReadyState özelliğini kontrol ederek Complete olana kadar kısır bir döngüye sokuyoruz. Döngü içerisinde kullandığımız Application.DoEvents() kodu döngü süresince WebBrowser'daki yüklemenin kilitlenmemesini ve devam etmesini sağlayacak. Bir sonraki sorunumuz aslında çok daha kritik. WebBrowser objesi özünde bir COM objesi olduğu için Single Threaded bir Thread içerisinde çalıştırılması gerekiyor. Bu aslında biraz da işimize yarar, böylece tüm bu yükleme kodlarını başka bir Thread içerisine almış oluruz.


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

Yukarıda bir Thread tanımı yer alıyor. Bu tanım içerisinde en önemli nokta ikinci satırdaki kod. Yarattığımız Thread için SetApartmentState diyerek çalışma şeklini SingleThreadedApartment haline çeviriyoruz. Tüm sorunları aştık, geriye kaldı gelen ekran görüntüsünü uygun şekilde küçültme kodumuza. Bu işlem için çok farklı teknikler kullanılabilir, ben aşağıda bu tekniklerden birine ait kodu paylaşacağım. Makalemizin esas konusu bu olmadığı için detaya girmeyeceğim.

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)

Şimdi gelin tüm bu kodları birleştirerek bir Class yapısı içerisinde projelerimizde kullanacağımız kodumuzu inceleyelim.

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

Yarattığımız iki classtan biri olan GetImage programcıdan alacağı ekran görüntüsüne ait çözünürlük bilgilerini ve geri döndüreceği resmin boyut bilgilerini alıyor. Aldığı bilgiler çerçevesinde ekran görüntüsünün alınması sonrasında da görüntünün istenen boyuta küçültülmesini sağlayacak. WebPageBitmap class'ımız ise ekran görüntüsünün alınmasından ve sonrasında da tekrar boyutlandırılmasında sorumlu. Tabi tüm bu işlemleri ayrı bir Thread içerisinde yapmamız gerektiğinden bahsetmiştik. Şimdi de gelin yukarıdaki Class'ımızı sayfamızda nasıl kullanabileceğimize bakalım.

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()
        While NewTh.ThreadState = Threading.ThreadState.Running
        End While
        Image1.ImageUrl = TextBox1.Text.Replace(".", "_") & ".jpg"
    End Sub
 
    Sub DoIT()
        Try
            Dim thumb As New GetSiteThumbnail.GetImage("http://" & TextBox1.Text, 1024, 768, 320, 240)
            Dim x As System.Drawing.Bitmap = thumb.GetBitmap()
            x.Save(Server.MapPath(".") & "\" & TextBox1.Text.Replace(".", "_") & ".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()
        Finally
        End Try
    End Sub
End Class 

Code-Behind dosyamızın kodunu yukarda inceleyebilirsiniz. Button1'e tıklandığında uygun Thread'i yaratarak DoIT Sub'ını çalıştıracağı şekilde hazırlıyoruz. Basit bir şekilde Thread'in bitmesini beklemek için yine kısır bir döngü kullanarak ThreadState durumunu kontrol ettik. Ekran görüntüsü alındığı gibi kullanıcıya göstermek için biraz kullanıcıyı bekletmemiz gerekecek. Eğer siz bir AJAX web sitesi hazırlıyorsanız ekran görüntüsünün alınmasını beklemektense bir AJAX Timer ile durumu kontrol edip kullanıcıya bilgi verebilirsiniz.

DoIT Sub'ımız içerisine bakarsak aslında bir önceki Class yapımızı kullandığımızı görüyoruz. Ben kodumda ekran görüntüsünü ve oluşursa herhangi bir hatayı farklı yerlere kaydettim. Siz bu konumları değiştirerek farklı yapılar kurabilirsiniz.

Tüm bu kodlara ek olarak daha yüksek performanslı bir sonuç için kodunuzun daha önce alınmış ekran görüntülerini kontrol ederek eğer söz konusu adrese ait ekran görüntüsü çok eski değilse yenisini almadan kullanıcıya eskisi de göstermesini sağlayabilirsiniz.

Hepinize kolay gelsin.

Daron Yöndem
MCPD, MCITP, MCTS, MCSD, MCAD
MCDBA, MCP, ACP, ICSD, IEL'03
http://daron.yondem.com

Makalemize ait web sitesi ekran görüntüsünü alan kodlarımız ile kodlarımızı kullanan örnek bir uygulaya ait Visual Studio projesini buradan indirebilrisiniz.