2

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

我尝试重写 WebClient 类来实现超时,但无论我将超时设置为什么值,它都不会超时!这是我尝试过的代码,在另一个 stackoverflow 答案中找到

using System;
using System.Net;

public class WebDownload : WebClient
{
    /// <summary>
    /// Time in milliseconds
    /// </summary>
    public int Timeout { get; set; }

    public WebDownload() : this(60000) { }

    public WebDownload(int timeout)
    {
        this.Timeout = timeout;
    }

    protected override WebRequest GetWebRequest(Uri address)
    {
        var request = base.GetWebRequest(address);
        if (request != null)
        {
            request.Timeout = this.Timeout;
        }
        return request;
    }
}

然后,调用使用:

 WebDownload webClient = new WebDownload(20000);
 try 
 {               
      webClient.DownloadFile(url, tmpFile);
 }
 catch {
     //throw error
 }

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

4

2 回答 2

1

创建一个扩展方法怎么样?

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 + "\"");
        }
    }
}
于 2013-10-18T23:51:36.687 回答
0

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

至于下载文件,您可以使用HttpWebRequestgetHttpWebResponse和 from it 使用方法GetResponseStream()来获取数据流。

这将很有帮助: 使用 HttpWebResponse 编码问题

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

编辑:一段有用的代码

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() );
}

请记住,此代码仍然容易受到您可能遇到的其他异常的影响。

于 2013-10-18T21:23:23.597 回答