2014-05-17 126 views
2

我有一個ASP.Net網站,我正在從遠程站點下載大型zip文件到服務器。此文件是而不是轉移到客戶端,但將保留在服務器上。我想爲使用SignalR的用戶提供進度更新。當我使用下面的代碼:在SignalR Hub中提供下載進度

public class InstallController : Hub 
{ 
    public void Send(string message) 
    { 
     Clients.All.AddMessage(message); 
    } 

    public void FileDownload() 
    { 
     WebClient client = new WebClient(); 
     client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged); 
     client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted); 
     client.DownloadFileAsync(new Uri("http://someserver.com/install/file.zip"), @"\file.zip"); 

    } 

    /* callbacks for download */ 
    void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) 
    { 
     double bytesIn = double.Parse(e.BytesReceived.ToString()); 
     double totalBytes = double.Parse(e.TotalBytesToReceive.ToString()); 
     double percentage = bytesIn/totalBytes * 100; 

     this.Send(String.Format("Download progress: {0}%", percentage.ToString())); 
    } 

    void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) 
    { 
     this.Send("Finished downloading file..."); 
    }  
} 

我得到異常:

一種類型的「System.InvalidOperationException」發生在 System.Web.dll中,但在用戶代碼

沒有處理異常

其他信息:此時,異步操作無法在 處啓動。異步操作只能在 異步處理程序或模塊內或在生命週期中的某些事件期間啓動。如果在執行頁面時發生此異常,請確保 頁面標記爲<%@ Page Async =「true」%>。這個異常可能是 也表示嘗試調用「async void」方法,該方法在ASP.NET請求處理中通常不支持 。相反,異步方法應該返回一個Task,並且調用者應該等待 它。

我見過幾個提到使用HttpClient而不是WebClient,但我沒有看到如何從中獲得進展。

+0

你可以發佈完整的代碼:至少在這個代碼駐留的方法聲明? –

+0

你也不應該使用signalR來解決這個問題。 並且你能否澄清這句話: _I正在從遠程站點下載一個大的zip文件到服務器。當你只是想讓客戶端下載它時,爲什麼要在服務器上下載文件!提供他的鏈接,而不是。 –

+0

該文件將保留在服務器上。它用於安裝服務器組件。 – edmistj

回答

2

「這是所有關於的SynchronizationContext」
http://msdn.microsoft.com/en-us/magazine/gg598924.aspx
這句話成爲繼.NET中增加了新的技術和功能相當普遍。

簡單。有幾個組件,如BackgroundWorkerWebClient,這就是隱藏SynchronizationContext來捕獲和使用,這意味着你需要尊重請求的生命週期中,ASP.NET組件的生命週期。

具體說來,HTTP方法(GET和POST)總是以同樣的方式工作,客戶端向服務器提交HTTP請求,然後服務器向客戶端返回響應,應用程序將嘗試確保發生這種情況時,ASP.NET的SynchronizationContext就是爲此設計的。

的更多信息:

即使使用SignalR請求包含相同的ASP.NET SynchronizationContext,因爲它,你需要工作「外「當前SynchronizationContext或以正確的方式使用它。

SignalR被設計爲使用異步編程,使用TPL爲默認值,你可以利用它的好處,在http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-server#asyncmethodshttp://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-server#asyncclient

檢查您可以通過多種方式解決你的問題。
如果你想使用SignalR來顯示進度..我會做類似下面的代碼(我仍然使用.NET 4.0,但使用TaskAsync方法的.NET 4.5更容易)。

public Task<string> FileDownload() 
{ 
    var client = new WebClient(); 
    client.DownloadProgressChanged += (sender, args) => client_DownloadProgressChanged(sender, args, this.Context.ConnectionId); 
    client.DownloadFileAsync(new Uri("https://epub-samples.googlecode.com/files/cc-shared-culture-20120130.epub"), @"C:\temp\file.zip"); 

    var result = new TaskCompletionSource<string>(); 
    AsyncCompletedEventHandler clientOnDownloadFileCompleted = (sender, args) => 
    { 
     client.Dispose(); 
     if (args.Error != null) 
     { 
      result.SetException(args.Error); //or result.SetResult(args.Error.Message); 
      return; 
     } 
     result.SetResult("Downloaded"); 
    }; 

    client.DownloadFileCompleted += clientOnDownloadFileCompleted; 
    return result.Task; 
} 

private static void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e, 
                string connectionId) 
{ 
    GlobalHost.ConnectionManager.GetHubContext<SomeHub>() 
       .Clients.Client(connectionId) 
       .NotifyProgress(e.ProgressPercentage); 
} 

請記住,這只是一個例子,你可以改善他們對待斷開,和取消,當中可能發生的其它事情的方式(取決於您的應用程序邏輯)。

此外,也可以使用一個 「的解決方法」(不推薦):

的代碼將是非常相似於上述。

+0

我得到了同樣的異常「在System.Web.dll中發生類型'System.InvalidOperationException'的異常,但未在用戶代碼中處理」在線client.DownloadFileAsync(new Uri(「https:// epub-samples。 googlecode.com/files/cc-shared-culture-20120130.epub「),@」C:\ temp \ file.zip「); – edmistj

+0

這將取決於你什麼時候調用該方法..我寫他直接由SignalR客戶端調用。 –

+0

直接從客戶端調用它時,我會得到相同的異常。 – edmistj