2011-12-12 39 views
3

我所希望做的是:流控制檯應用程序的標準輸出到一個ASP.NET MVC視圖

1)從一個MVC視圖,啓動一個長期運行的進程。就我而言,這個過程是一個獨立的控制檯應用程序正在執行。控制檯應用程序可能運行30分鐘,並正在進行Console.Write的當前操作。

2)回到MVC視圖,定期輪詢服務器以檢索我已重定向到Stream(或任何我可以訪問它的任何地方)的最新Standard Out。我會將新的retieved標準輸出附加到日誌文本框或其他相當的東西。

聽起來相對容易。儘管我的客戶端編程有點生疏,但我遇到了實際流媒體問題。我會認爲這不是一項不常見的任務。任何人都可以在ASP.NET MVC中獲得體面的解決方案嗎?

最大的問題似乎是,我不能得到StandardOutput直到執行結束,但我能夠得到它與事件處理程序。當然,使用事件處理程序似乎失去了我輸出的重點。

這是我與迄今的工作...

public ActionResult ProcessImport() 
    { 
     // Get the file path of your Application (exe) 
     var importApplicationFilePath = ConfigurationManager.AppSettings["ImportApplicationFilePath"]; 

     var info = new ProcessStartInfo 
     { 
      FileName = importApplicationFilePath, 
      RedirectStandardError = true, 
      RedirectStandardInput = true, 
      RedirectStandardOutput = true, 
      CreateNoWindow = true, 
      WindowStyle = ProcessWindowStyle.Hidden, 
      UseShellExecute = false 
     }; 

     _process = Process.Start(info); 
     _process.BeginOutputReadLine(); 

     _process.OutputDataReceived += new DataReceivedEventHandler(_process_OutputDataReceived); 

     _process.WaitForExit(1); 

     Session["pid"] = _process.Id; 

     return Json(new { success = true }, JsonRequestBehavior.AllowGet); 
    } 

    void _process_OutputDataReceived(object sender, DataReceivedEventArgs e) 
    { 
     _importStandardOutputBuilder.Insert(0, e.Data); 
    } 

    public ActionResult Update() 
    { 
     //var pid = (int)Session["pid"]; 
     //_process = Process.GetProcessById(pid); 

     var newOutput = _importStandardOutputBuilder.ToString(); 
     _importStandardOutputBuilder.Clear(); 

     //return View("Index", new { Text = _process.StandardOutput.ReadToEnd() }); 
     return Json(new { output = newOutput }, "text/html"); 
    } 

我沒有寫客戶端代碼卻因爲我只是擊中URL測試行動,但我也有興趣你將如何處理這個文本的輪詢。如果你也可以提供這個實際的代碼,那就太好了。我會假設你將有一個js循環在開始使用ajax調用服務器的過程之後運行,該服務器返回JSON結果......但是,它不是我的特長,所以很想看看它是如何完成的。

謝謝!

回答

4

對,從我收到的幾條建議以及大量的試驗和錯誤中,我已經提出了一個正在進行的解決方案,並認爲我應該與大家分享。目前肯定存在潛在的問題,因爲它依賴於整個網站共享的靜態變量,但是對於我的要求,它很好地完成了這項工作。開始!

讓我們從我的觀點出發吧。我們首先將我的按鈕的click事件綁定到一些jquery上,這個jquery發佈到/ Upload/ProcessImport(上傳是我的MVC Controller和ProcessImport是我的MVC Action)。流程導入啓動我的流程,我將在下面詳細介紹。然後js在調用js函數getMessages之前等待一小段時間(使用setTimeout)。

所以getMessages在按鈕被點擊後被調用,並且它對/ Upload/Update(我的更新操作)做了一個帖子。 Update操作基本上檢索Process的狀態並返回它以及自上次調用Update之後的StandardOutput。然後,getMessages將解析JSON結果並將StandardOutput附加到我的視圖中的列表中。我也嘗試滾動到列表的底部,但這並不完美。最後,getMessages檢查進程是否完成,如果沒有,它會每秒鐘直接調用它自己,直到它已經完成。

<script type="text/javascript"> 

    function getMessages() { 

     $.post("/Upload/Update", null, function (data, s) { 

      if (data) { 
       var obj = jQuery.parseJSON(data); 

       $("#processOutputList").append('<li>' + obj.message + '</li>'); 
       $('#processOutputList').animate({ 
        scrollTop: $('#processOutputList').get(0).scrollHeight 
       }, 500); 
      } 

      // Recurivly call itself until process finishes 
      if (!obj.processExited) { 
       setTimeout(function() { 
        getMessages(); 
       }, 1000) 
      } 
     }); 
    } 

    $(document).ready(function() { 

     // bind importButton click to run import and then poll for messages 
     $('#importButton').bind('click', function() { 
      // Call ProcessImport 
      $.post("/Upload/ProcessImport", {}, function() { }); 

      // TODO: disable inputs 

      // Run's getMessages after waiting the specified time 
      setTimeout(function() { 
       getMessages(); 
      }, 500) 
     }); 

    }); 

</script> 

<h2>Upload</h2> 

<p style="padding: 20px;"> 
    Description of the upload process and any warnings or important information here. 
</p> 

<div style="padding: 20px;"> 

    <div id="importButton" class="qq-upload-button">Process files</div> 

    <div id="processOutput"> 
     <ul id="processOutputList" 
      style="list-style-type: none; margin: 20px 0px 10px 0px; max-height: 500px; min-height: 500px; overflow: auto;"> 
     </ul> 
    </div> 

</div> 

控制器。我選擇不使用AsyncController,主要是因爲我發現我不需要。我最初的問題是將我的控制檯應用程序的StdOut傳送到視圖。我發現標準輸出不能ReadToEnd,所以將事件處理程序ProcessOutputDataReceived掛起來,當標準輸出數據被接收後,它會被觸發,然後使用StringBuilder,將輸出附加到先前接收到的輸出。這種方法的問題在於,控制器在每篇文章中都得到了重新證實,爲了克服這個問題,我決定讓應用程序的Process和StringBuilder保持靜態。這使我可以接收到更新操作的調用,獲取靜態StringBuilder並有效地將其內容清空回到我的視圖。我還發回一個布爾值來指示進程是否已退出,以便視圖可以在知道此操作時停止輪詢。此外,靜態我試圖確保如果進行中的進口,不要讓其他人開始。

public class UploadController : Controller 
{ 
    private static Process _process; 
    private static StringBuilder _importStandardOutputBuilder; 

    public UploadController() 
    { 
     if(_importStandardOutputBuilder == null) 
      _importStandardOutputBuilder = new StringBuilder(); 
    } 

    public ActionResult Index() 
    { 
     ViewData["Title"] = "Upload"; 
     return View("UploadView"); 
    } 

    //[HttpPost] 
    public ActionResult ProcessImport() 
    { 
     // Validate that process is not running 
     if (_process != null && !_process.HasExited) 
      return Json(new { success = false, message = "An Import Process is already in progress. Only one Import can occur at any one time." }, "text/html"); 

     // Get the file path of your Application (exe) 
     var importApplicationFilePath = ConfigurationManager.AppSettings["ImportApplicationFilePath"]; 

     var info = new ProcessStartInfo 
     { 
      FileName = importApplicationFilePath, 
      RedirectStandardError = true, 
      RedirectStandardInput = true, 
      RedirectStandardOutput = true, 
      CreateNoWindow = true, 
      WindowStyle = ProcessWindowStyle.Hidden, 
      UseShellExecute = false 
     }; 

     _process = Process.Start(info); 
     _process.BeginOutputReadLine(); 
     _process.OutputDataReceived += ProcessOutputDataReceived; 
     _process.WaitForExit(1); 

     return Json(new { success = true }, JsonRequestBehavior.AllowGet); 
    } 

    static void ProcessOutputDataReceived(object sender, DataReceivedEventArgs e) 
    { 
     _importStandardOutputBuilder.Append(String.Format("{0}{1}", e.Data, "</br>")); 
    } 

    public ActionResult Update() 
    { 
     var newOutput = _importStandardOutputBuilder.ToString(); 
     _importStandardOutputBuilder.Clear(); 
     return Json(new { message = newOutput, processExited = _process.HasExited }, "text/html"); 
    } 
} 

那麼,就是這樣。有用。它仍然需要工作,所以希望當我完善我的時候,我會更新這個解決方案。對靜態方法有什麼想法(假定業務規則是一次只能有一個導入)?

+0

這是一個很好的基礎解決方案。我在自己的代碼中使用它作爲jumpboard,在那裏我必須解決「靜態方法」,因爲我必須假設我的網站被多個用戶同時使用,並且每個用戶都可以運行一個進程時間。所以控制器中的靜態過程對象不會爲我做。因此我寫了一個圍繞Process類的封裝,我以鬆散耦合的方式將其放入用戶會話狀態(參見http://josephwoodward.co.uk/2014/06/sessions-asp-net-mvc-using-dependency -注射/)。 –

1

調查長民意調查。基本上你可以打開一個Ajax請求,然後在控制器內部持有它。

Sample of long poll

這是東西,你會想要做異步或者你可能會與線程匱乏的問題。

+0

很酷。我會檢查出來的。我還沒有使用AsyncControllers的經驗。他們的示例看起來像是在做我想要的東西...但我仍然需要了解在處理控制檯應用程序時是否可以獲得StandardOut。 – Arkiliknam

+0

我會在控制檯應用程序管理其輸出的網站上託管一個端點。然後這可以推送到一個可觀察的集合中,該集合可以將輸出推送給長輪詢客戶端。 – RubbleFord

0

考慮編寫一個在某個服務器上運行的服務,並將其輸出管道輸出到您的Web服務器可訪問的文件/數據庫。然後,您可以將生成的數據加載到您的網站並將其返回給您的呼叫者。

瞭解長時間捆綁Web服務器的線程可能導致線程匱乏,並使其看起來像您的網站已崩潰(即使它正在忙着等待您的控制檯應用程序運行)。

相關問題