2017/1/1

擷取網站縮圖 - 使用 ASP.NET MVC 5 / Fetch thumbnail image from website with ASP.NET MVC 5



剛好這兩天完成這功能,順便打一篇教學好了...

View:

<img src="@Url.Action("GetWebThumbnail", new { url = ContextUrl, width = 1080, height = 900, thumbWidth = 700, thumbHeight = 650 })" />


沒錯... 一行就好。
用 Url.Action() 指定 src 和記得帶參數過去就好。

  • url : 要擷取縮圖的網址 // 千萬不要帶入 google 或 facebook,會得到一道白光...
  • width : 擷取網頁的寬
  • height : 擷取網頁的高
  • thumbWidth : 縮圖的寬
  • thumbHeight : 縮圖的高


Controller:

控制器內藉由 ThumbnailService.GetThumbnail() 方法擷取到網頁縮圖後,再轉成 Bytes 回傳給 View。

 前端 View 的 img 就能直接把 Action 回傳的東西當作 source。

        public ActionResult GetWebThumbnail(string url, int width, int height, int thumbWidth, int thumbHeight)
        {
            var image = ThumbnailService.GetThumbnail(url, width, height, thumbWidth, thumbHeight);

            var imageBytes = ImageToBytes(image); //Convert image into a byte array
            
            return File(imageBytes, "image/Jpeg"); //Return as file result
            
        }


        private static byte[] ImageToBytes(Image img)
        {
            MemoryStream stream = new MemoryStream();

            img.Save(stream, ImageFormat.Jpeg);

            return stream.ToArray();

        }




ThumbnailService.cs:

所以說,重點在這裡...

其實是利用 Windows.Form 的元件 WebBrowser 來完成擷圖這件事。

記得先在專案的參考 (reference) 加入

  • System.Windows.Forms
  • System.Windows.Forms.DataVisualization 

using System;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;

namespace 你的名字
{
    public class ThumbnailService
    {
        protected string _url;
        protected int _width, _height;
        protected int _thumbWidth, _thumbHeight;
        protected Bitmap _bmp;

        public static  Image GetThumbnail(string url, int width, int height,
          int thumbWidth, int thumbHeight)
        {
            ThumbnailService thumbnail = new ThumbnailService(url, width, height,
              thumbWidth, thumbHeight);

            return thumbnail.GetThumbnail();
        }
        
        //建構子
        protected ThumbnailService(string url, int width, int height,
          int thumbWidth, int thumbHeight)
        {
            _url = url;
            _width = width;
            _height = height;
            _thumbWidth = thumbWidth;
            _thumbHeight = thumbHeight;
        }
        

        protected Image GetThumbnail()
        {
            // WebBrowser 是 ActiveX 控制項,一定要在單一執行緒執行
            // 所以這邊產生一個 Thread 給他使用

            Thread thread = new Thread(new ThreadStart(GetThumbnailWorker));
            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();
            thread.Join();
            return  _bmp.GetThumbnailImage(_width, _height, null, IntPtr.Zero);
        }
        
        protected void GetThumbnailWorker()
        {
            WebBrowser browser = new WebBrowser();

            browser.Navigate(@_url);
            browser.ScriptErrorsSuppressed = true;
            browser.ScrollBarsEnabled = false;
            browser.Size = new Size(_width, _height);

            // 等待網頁讀取完畢
            while (browser.ReadyState != WebBrowserReadyState.Complete)
                Application.DoEvents();
            
            _bmp = new Bitmap(_thumbWidth, _thumbHeight);
            
            browser.DrawToBitmap(_bmp, new Rectangle(browser.Location.X, browser.Location.Y,
              _thumbWidth, _thumbHeight));

        }

    }

}


重點在於最後兩個方法,

GetThumbnail 和 GetThumbnailWorker

其中 WebBrowser 這元件必須在單一執行緒執行,所以在 GetThumbnail 內做了一條執行緒給他,並執行 GetThumbnailWorker。

GetThumbnailWorker 要注意程式必須等待 WebBrowser 將網頁讀取完畢 (不然 DrawToBitmap 會畫不出東西...)。

其中還可以注意的是 45 行處,C# Image 類別有內建縮圖 GetThumbnailImage() 方法可用,將擷取的圖再做一次縮圖降低圖片大小後回傳。


缺點:

查過無數資料後,若你要做到像 Facebook 那樣貼連結就快速得到縮圖似乎需要很強大的演算法...在 C# 內我們借用 WebBrowser 物件先讀取網址後再畫進 Bitmap。

缺點就是,速度有點慢...畢竟要等 WebBrowser 將整個網頁讀完再做畫圖處理。

有機會找到解法再補上好了...

若你是寫 Webform 的玩家,也可以將 ThumbnailService.cs 內的程式寫在 ashx 內,用網址帶入 querystring 再回傳給 aspx 上的 Image 元件就好 ( 應該是這樣,好幾年沒碰  Webform了...

沒有留言:

張貼留言