我试图下载一个客户端的数据到我的本地机器(编程),他们的web服务器非常非常慢,这导致我的WebClient对象超时。
这是我的代码:
WebClient webClient = new WebClient();
webClient.Encoding = Encoding.UTF8;
webClient.DownloadFile(downloadUrl, downloadFile);
有没有办法在这个对象上设置无限超时?如果不是,有没有人能帮我举个例子告诉我另一种方法?
该URL在浏览器中工作正常-只需要大约3分钟就能显示出来。
用法:
using (var client = new TimeoutWebClient(TimeSpan.FromSeconds(10)))
{
return await client.DownloadStringTaskAsync(url).ConfigureAwait(false);
}
类:
using System;
using System.Net;
namespace Utilities
{
public class TimeoutWebClient : WebClient
{
public TimeSpan Timeout { get; set; }
public TimeoutWebClient(TimeSpan timeout)
{
Timeout = timeout;
}
protected override WebRequest GetWebRequest(Uri uri)
{
var request = base.GetWebRequest(uri);
if (request == null)
{
return null;
}
var timeoutInMilliseconds = (int) Timeout.TotalMilliseconds;
request.Timeout = timeoutInMilliseconds;
if (request is HttpWebRequest httpWebRequest)
{
httpWebRequest.ReadWriteTimeout = timeoutInMilliseconds;
}
return request;
}
}
}
但我推荐一个更现代的解决方案:
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
public static async Task<string> ReadGetRequestDataAsync(Uri uri, TimeSpan? timeout = null, CancellationToken cancellationToken = default)
{
using var source = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
if (timeout != null)
{
source.CancelAfter(timeout.Value);
}
using var client = new HttpClient();
using var response = await client.GetAsync(uri, source.Token).ConfigureAwait(false);
return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
}
它将在超时后抛出OperationCanceledException异常。
我昨天不得不与这个问题作斗争,我也最终写了我的自定义扩展类。
通过查看下面的代码并将其与接受的答案进行比较,您可以看到,我试图稍微调整建议,以便拥有一个更通用的类:通过这种方式,您可以在实例化对象时或在使用使用内部WebRequest处理程序的方法之前设置精确的超时。
using System;
using System.Net;
namespace Ryadel.Components.Web
{
/// <summary>
/// An extension of the standard System.Net.WebClient
/// featuring a customizable constructor and [Timeout] property.
/// </summary>
public class RyadelWebClient : WebClient
{
/// <summary>
/// Default constructor (30000 ms timeout)
/// NOTE: timeout can be changed later on using the [Timeout] property.
/// </summary>
public RyadelWebClient() : this(30000) { }
/// <summary>
/// Constructor with customizable timeout
/// </summary>
/// <param name="timeout">
/// Web request timeout (in milliseconds)
/// </param>
public RyadelWebClient(int timeout)
{
Timeout = timeout;
}
#region Methods
protected override WebRequest GetWebRequest(Uri uri)
{
WebRequest w = base.GetWebRequest(uri);
w.Timeout = Timeout;
((HttpWebRequest)w).ReadWriteTimeout = Timeout;
return w;
}
#endregion
/// <summary>
/// Web request timeout (in milliseconds)
/// </summary>
public int Timeout { get; set; }
}
}
当我在那里时,我也抓住机会将默认的Timeout值降低到30秒,因为100秒对我来说似乎太多了。
如果你需要关于这个类或如何使用它的额外信息,请查看我在博客上写的这篇文章。
根据kisp解决方案,这是我的编辑版本工作异步:
类WebConnection.cs
internal class WebConnection : WebClient
{
internal int Timeout { get; set; }
protected override WebRequest GetWebRequest(Uri Address)
{
WebRequest WebReq = base.GetWebRequest(Address);
WebReq.Timeout = Timeout * 1000 // Seconds
return WebReq;
}
}
异步任务
private async Task GetDataAsyncWithTimeout()
{
await Task.Run(() =>
{
using (WebConnection webClient = new WebConnection())
{
webClient.Timeout = 5; // Five seconds (the multiplication is in the override)
webClient.DownloadData("https://www.yourwebsite.com");
}
});
} // await GetDataAsyncWithTimeout()
否则,如果你不想使用async:
private void GetDataSyncWithTimeout()
{
using (WebConnection webClient = new WebConnection())
{
webClient.Timeout = 5; // Five seconds (the multiplication is in the override)
webClient.DownloadData("https://www.yourwebsite.com");
}
} // GetDataSyncWithTimeout()