Asp.net 在信号中心内提供下载进度

Asp.net 在信号中心内提供下载进度,asp.net,signalr,Asp.net,Signalr,我有一个ASP.Net网站,我正在从远程站点下载一个大的zip文件到服务器。此文件未传输到客户端,但将保留在服务器上。我想使用SignalR向用户提供进度更新。当我使用下面的代码时: public class InstallController : Hub { public void Send( string message ) { Clients.All.AddMessage( message ); } public void FileDown

我有一个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,但未在用户代码中处理

其他信息:异步操作无法在启动时启动 这一次。异步操作只能在 异步处理程序或模块,或在页面中的某些事件期间 生命周期。如果在执行页面时发生此异常,请确保 这一页有标记。这一例外可能会发生 还指示调用“async void”方法的尝试,该方法是 ASP.NET请求处理中通常不支持。相反 异步方法应返回任务,调用方应等待 它

我曾多次提到使用HttpClient而不是WebClient,但我不知道如何从中获得进展。

“这都是关于SynchronizationContext的”

自从.NET中添加了新技术和功能以来,这个短语变得非常普遍

简言之。。有几个组件,例如
BackgroundWorker
WebClient
,它们将
SynchronizationContext
隐藏到捕获和使用中,这意味着您需要尊重请求的生命周期、ASP.NET组件的生命周期

具体来说,HTTP方法(GET和POST)始终以相同的方式工作,客户端向服务器提交HTTP请求,然后服务器向客户端返回响应,应用程序将尝试确保发生这种情况,ASP.NET的
SynchronizationContext
就是为此而设计的

更多信息:

即使使用signar的请求也包含相同的ASP.NET
SynchronizationContext
,因此您需要在当前的
SynchronizationContext
之外工作,或者以正确的方式使用它

SignalR设计为使用异步编程,默认使用,您可以利用它的优势,签入和

你可以用很多方法解决你的问题。
如果要使用信号器显示进度。。我会像下面的代码那样做(我仍然使用.NET4.0,但是使用.NET4.5和TaskAsync方法更容易)

公共任务文件下载()
{
var client=new WebClient();
client.DownloadProgressChanged+=(发送方,args)=>client\u DownloadProgressChanged(发送方,args,this.Context.ConnectionId);
client.DownloadFileAsync(新Uri(“https://epub-samples.googlecode.com/files/cc-shared-culture-20120130.epub),@“C:\temp\file.zip”);
var result=new TaskCompletionSource();
AsyncCompletedEventHandler clientOnDownloadFileCompleted=(发送方,参数)=>
{
client.Dispose();
如果(args.Error!=null)
{
result.SetException(args.Error);//或result.SetResult(args.Error.Message);
返回;
}
result.SetResult(“下载”);
};
client.DownloadFileCompleted+=clientOnDownloadFileCompleted;
返回结果。任务;
}
私有静态无效客户端\u DownloadProgressChanged(对象发送方,DownloadProgressChangedEventArgs e,
字符串连接ID)
{
GlobalHost.ConnectionManager.GetHubContext()
.Clients.Client(connectionId)
.进度(如进度百分比);
}
请记住,这只是一个示例,您可以改进他们处理断开连接和取消的方式,以及其他可能发生的事情(取决于您的应用程序逻辑)

也可以使用“变通方法”(不推荐):


该代码与上面的代码非常相似。

您可以发布完整的代码吗:至少是该代码所在的方法声明?您也不应该使用signar解决该问题。。你能澄清一下这句话吗:我正在从一个远程站点下载一个大的zip文件到服务器。当你只想让客户端下载文件时,为什么还要在服务器上下载呢!改为向他提供链接。该文件将保留在服务器上。它用于安装服务器组件。您是否在按钮单击事件中下载文件??你能分享一下你的.aspx页面的结构吗??我想在按钮点击事件上下载文件,并在我的应用程序中显示实时进度条。这是使用您的上述代码发生的吗?我在client.DownloadFileAsync(新Uri(“),@“C:\temp\file.zip”)行上得到相同的异常“System.Web.dll中发生了类型为'System.InvalidOperationException'的异常,但未在用户代码中处理”;这将取决于您调用该方法的时间。.我编写了它,让它直接由SignalR客户端调用。直接从客户端调用它时,我会遇到相同的异常。@edmistj这是意外行为,您可以尝试将WebClient代码抛出到内部:Task.Factory.StartNew(()=>{/*code here*/});谢谢你的努力。最后,我想我可能会同意这个解决方案,但我感谢你的时间,它确实奏效了。
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);
}