我有一个ASP.Net网站,我从一个远程站点下载一个大的zip文件到服务器。此文件不已传输到客户端,但仍保留在服务器上。我想使用SignalR向用户提供进度更新。当我使用下面的代码时:
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 :(得分:2)
“这是所有关于SynchronizationContext”
http://msdn.microsoft.com/en-us/magazine/gg598924.aspx
自从在.NET中添加新技术和新功能以来,这句话变得非常普遍。
简要..有几个组件,例如BackgroundWorker
和WebClient
,它们将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#asyncmethods和http://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);
}
请记住,这只是一个示例,您可以改进它们处理断开连接和取消的方式,以及可能发生的其他事情(取决于您的应用程序逻辑)。
也可以使用“解决方法”(不推荐):
代码与上面的内容非常相似。