非同步AsyncController + 網頁截圖 實作教學

非同步AsyncController + 網頁截圖 實作教學

有時候在Server端處理一些比較耗時的工作,例如讀寫檔案,呼叫外部的服務等..

不僅僅讓使用者Hold在那邊,同時也占了一個Thread,當Thread到達上限後,之後進入的使用者

就只能排在佇列中,當佇列也額滿時,就會回覆503給使用者了。(買五月天演唱會的票時常會遇到)

之前的部落格有去呼叫網頁截圖的API,剛好可以用來練習這樣的情境:

 

網路上有很多提供類似擷取網頁圖片的服務,使用上只要在網址上加上參數即可,很簡單。

因此只要利用HttpWebRequest這個類別去送出一個request,然後將回應的圖片存起來即可。

C#


public class HomeController : AsyncController
    {
        [AsyncTimeout(10000)]
        public void LoadImageAsync(string url)
        {
            string name = Guid.NewGuid().ToString();   //設定一個檔名
            string path = Server.MapPath("~/File/links/");  //存放路徑
 
            AsyncManager.OutstandingOperations.Increment(1);  //計數器加 1
            AsyncManager.Parameters["Status"] = true;     //預設一個參數叫Status,值為true
 
            WebRequest httpRequest =
                    HttpWebRequest.Create(@"http://mozshot.nemui.org/shot/large?" + url);
            //建立一個Request
 
            //以下是參考的服務網址
            //http://capture.heartrails.com/256x256/shadow/delay=5?指定的url  較快
            //http://mozshot.nemui.org/shot/large?指定的url  較慢
 
            httpRequest.BeginGetResponse((ar) =>
            {
                WebResponse response = httpRequest.EndGetResponse(ar);
                 
                //下面這一段是判斷抓到的是不是正確的圖片
                //因為服務有時候圖抓得不是那麼快
                if (false)//這邊需看不同的服務回傳的結果作判斷
                {
                    AsyncManager.Parameters["Message"] = "抓取失敗,請稍後再試一次。";
                    AsyncManager.OutstandingOperations.Decrement();
                    //當失敗的時候,計數器減1,也就是歸零,就會觸發LoadImageCompleted
                }
                else
                {
                  using (Stream dataStream = response.GetResponseStream())
                  {
                    //如果是抓到正確的response,將stream轉為圖片
                    using (System.Drawing.Image image = System.Drawing.Bitmap.FromStream(dataStream))
                    {
                        try
                        {
                            //存檔
                            image.Save(path + name + ".png");
                            //設定另外一個參數Message,存放訊息
                            AsyncManager.Parameters["Message"] = "抓取成功";
                        }
                        catch (Exception ex)
                        {
                            //發生錯誤時,將Status參數設為false
                            AsyncManager.Parameters["Status"] = false;
                        }
                        finally
                        {
                            //不管有沒有發生例外,都將計數器減1
                            AsyncManager.OutstandingOperations.Decrement();
                        }
                      }
                   }
                }
            }, null);
        }
 
        public ActionResult LoadImageCompleted(bool Status, string Message)
        {
            //回應結果,這邊寫這樣有點蠢,但因為我自己的網站是用ajax去呼叫的
            //所以結果會直接alert出來
            if (Status)
            {
                return Content(Message);
            }
            return Content("0");
        }
    }

Html


<script src="http://code.jquery.com/jquery-1.7.1.min.js" type="text/javascript"></script>

<script type="text/javascript">
    $(function () {
        $('#url').change(function () {
                //注意這邊呼叫的路徑!!不用加Async
                $.post('<%=Url.Action("LoadImage") %>', { url: $('#url').val() },
                    function (data, status) {
                        if (data == '0') {
                            alert("取樣失敗!");
                        } else {
                            alert(data);
                        }
                    });
            });
        });
</script>

輸入網址:<input id="url" type="text" /><br /><br />

 

另外上面那一段判斷response正不正確,每個服務回應的都不太一樣,在試的時候只要

偵個錯,看一下response有什麼不一樣,就可以做出判斷了。

 

參考資料:

 保哥的 ASP.NET MVC 開發心得分享 (18):非同步控制器開發

 截圖服務

 Using the ASP.NET MVC Futures AsyncController