C# Can';尝试使用WebClient.DownloadFile()或WebRequest下载文件时无法实现超时

C# Can';尝试使用WebClient.DownloadFile()或WebRequest下载文件时无法实现超时,c#,.net,asp.net-mvc-4,C#,.net,Asp.net Mvc 4,我正在尝试实现一些从URL下载文件的功能。但是,如果文件占用的时间超过30秒,我想取消下载,或者让它超时 我已经尝试重写WebClient类来实现超时,但是无论我将超时设置为什么值,它都不会超时!这是我试过的代码: 我还尝试使用WebRequest方法下载文件,并使用Timeout和ReadWriteTimeout属性,但没有骰子。这必须是一个非常常见的用例。感谢您的帮助。谢谢 您实现的超时与获取响应有关,但与包含所有数据的ResponseStream无关,通常需要更多时间才能实现。例如,获取响

我正在尝试实现一些从URL下载文件的功能。但是,如果文件占用的时间超过30秒,我想取消下载,或者让它超时

我已经尝试重写WebClient类来实现超时,但是无论我将超时设置为什么值,它都不会超时!这是我试过的代码:


我还尝试使用WebRequest方法下载文件,并使用Timeout和ReadWriteTimeout属性,但没有骰子。这必须是一个非常常见的用例。感谢您的帮助。谢谢

您实现的超时与获取响应有关,但与包含所有数据的ResponseStream无关,通常需要更多时间才能实现。例如,获取响应通常需要不到1秒的时间,但下载网页内容可能需要几秒钟

至于下载文件,您可以使用
HttpWebRequest
获取
HttpWebResponse
并从中使用方法
GetResponseStream()
获取数据流

这将有助于:

尤其是
byte[]buffer
用于获取部分数据的部分,而不是
StreamReader
ReadToEnd()
方法。在将部分数据下载到缓冲区时,您可以根据超时日期时间检查当前日期时间,从而允许在超时日期时间之后取消下载

编辑:一段有用的代码

private byte[] DownloadFile( string uri, int requestTimeout, int downloadTimeout, out bool isTimeout, out int bytesDownloaded )
{
    HttpWebRequest request = WebRequest.Create( uri ) as HttpWebRequest;
    request.Timeout = requestTimeout;
    HttpWebResponse response = null;
    Stream responseStream = null;
    MemoryStream downloadedData = null;

    byte[] result = null;
    bytesDownloaded = 0;

    isTimeout = false;
    try
    {
        // Get response
        response = request.GetResponse() as HttpWebResponse;
        byte[] buffer = new byte[ 16384 ];

        // Create buffer for downloaded data
        downloadedData = new MemoryStream();

        // Set the timeout
        DateTime timeout = DateTime.Now.Add( new TimeSpan( 0, 0, 0, 0, downloadTimeout ) );

        // Read parts of the stream
        responseStream = response.GetResponseStream();
        int bytesRead = 0;
        DateTime now = DateTime.Now;
        while ( (bytesRead = responseStream.Read( buffer, 0, buffer.Length )) > 0 && DateTime.Now < timeout )
        {
            downloadedData.Write( buffer, 0, bytesRead );
            now = DateTime.Now;
            bytesDownloaded += bytesRead;
        }

        // Notify if timeout occured (could've been written better)
        if ( DateTime.Now >= timeout )
        {
            isTimeout = true;
        }
    }
    catch ( WebException ex )
    {
        // Grab timeout exception
        if ( ex.Status == WebExceptionStatus.Timeout )
        {
            isTimeout = true;
        }
    }
    finally
    {
        // Close the stream
        if ( responseStream != null )
        {
            responseStream.Close();
        }
    }

    if ( downloadedData != null )
    {
        result = downloadedData.GetBuffer();
        downloadedData.Close();
    }

    return result;
}

记住,对于您可能遇到的其他异常,此代码仍然容易受到攻击。

创建扩展方法如何

WebClient wc = new WebClient();
wc.DownloadFileWithTimeout(url, filename, 20000);

公共静态类SOExtensions
{
public static void DownloadFileWithTimeout(此WebClient wc、字符串url、字符串文件、int timeout)
{
var tcs=new TaskCompletionSource();
var bgTask=Task.Factory.StartNew(()=>
{
DownloadFileTaskAsync(url,file).Wait();
tcs.TrySetResult(真);
});
如果(!bgTask.Wait(超时))
{
wc.CancelAsync();
抛出新的TimeoutException(“下载\'+url+“\””时超时);
}
}
}

我尝试了你的代码,没有发现任何问题。正如您所期望的那样超时。我在HttpWebRequest上单独使用超时时遇到了各种问题,因为它没有涵盖有关下载实际数据的超时。OP的代码在我的机器上也不能正常工作。它尝试下载整个文件(测试下载6MB文件时超时为500ms,OP的方法下载需要1800ms)。
private byte[] DownloadFile( string uri, int requestTimeout, int downloadTimeout, out bool isTimeout, out int bytesDownloaded )
{
    HttpWebRequest request = WebRequest.Create( uri ) as HttpWebRequest;
    request.Timeout = requestTimeout;
    HttpWebResponse response = null;
    Stream responseStream = null;
    MemoryStream downloadedData = null;

    byte[] result = null;
    bytesDownloaded = 0;

    isTimeout = false;
    try
    {
        // Get response
        response = request.GetResponse() as HttpWebResponse;
        byte[] buffer = new byte[ 16384 ];

        // Create buffer for downloaded data
        downloadedData = new MemoryStream();

        // Set the timeout
        DateTime timeout = DateTime.Now.Add( new TimeSpan( 0, 0, 0, 0, downloadTimeout ) );

        // Read parts of the stream
        responseStream = response.GetResponseStream();
        int bytesRead = 0;
        DateTime now = DateTime.Now;
        while ( (bytesRead = responseStream.Read( buffer, 0, buffer.Length )) > 0 && DateTime.Now < timeout )
        {
            downloadedData.Write( buffer, 0, bytesRead );
            now = DateTime.Now;
            bytesDownloaded += bytesRead;
        }

        // Notify if timeout occured (could've been written better)
        if ( DateTime.Now >= timeout )
        {
            isTimeout = true;
        }
    }
    catch ( WebException ex )
    {
        // Grab timeout exception
        if ( ex.Status == WebExceptionStatus.Timeout )
        {
            isTimeout = true;
        }
    }
    finally
    {
        // Close the stream
        if ( responseStream != null )
        {
            responseStream.Close();
        }
    }

    if ( downloadedData != null )
    {
        result = downloadedData.GetBuffer();
        downloadedData.Close();
    }

    return result;
}
private void button1_Click( object sender, EventArgs e )
{
    bool isTimeout;
    int bytesDownloaded;
    byte[] data = DownloadFile( something, 1000,500, out isTimeout, out bytesDownloaded );

    MessageBox.Show( "Downloaded " + bytesDownloaded.ToString() + " bytes, Timeout = " + isTimeout.ToString() );
}
WebClient wc = new WebClient();
wc.DownloadFileWithTimeout(url, filename, 20000);
public static class SOExtensions
{
    public static void DownloadFileWithTimeout(this WebClient wc, string url, string file, int timeout)
    {
        var tcs = new TaskCompletionSource<bool>();

        var bgTask = Task.Factory.StartNew(() =>
        {
            wc.DownloadFileTaskAsync(url, file).Wait();
            tcs.TrySetResult(true);
        });


        if (!bgTask.Wait(timeout))
        {
            wc.CancelAsync();
            throw new TimeoutException("Timed out while downloading \"" + url + "\"");
        }
    }
}