在处理大文件上传时,有两个超时困扰着我们。HttpWebRequest.Timeout
and HttpWebRequest.ReadWriteTimeout
。我们需要解决both.
HttpWebRequest.ReadWriteTimeout
首先我们先来说说HttpWebRequest.ReadWriteTimeout
。我们需要禁用“写入流缓冲”。
httpRequest.AllowWriteStreamBuffering = false;
更改此设置后,您的HttpWebRequest.ReadWriteTimeout
这些值会神奇地按照您希望的方式工作,您可以将它们保留为默认值(5 分钟),甚至减少它们。我用的是60秒。
出现这个问题的原因是,当上传大文件时,如果数据被.NET框架缓冲,你的代码会认为上传完成,但实际上并没有完成,并调用HttpWebRequest.GetResponse()
太早了,会超时。
HttpWebRequest.Timeout
现在,我们来解决HttpWebRequest.Timeout
.
我们的第二个问题的出现是因为HttpWebRequest.Timeout
适用于整个上传。上传过程实际上包括三个步骤(这是一篇很棒的文章,是我的主要参考 http://www.thomaslevesque.com/2014/01/14/tackling-timeout-issues-when-uploading-large-files-with-httpwebrequest/):
- 请求start上传。
- 字节的写入。
- 请求finish上传并从服务器获取任何响应。
如果我们有一个适用于整个上传的超时,我们需要大量的超时来容纳大文件的上传,但我们也面临一个问题,即合法的超时将需要很长时间才能真正超时。这不是一个好情况。相反,我们希望在步骤 #1 和 #3 中应用较短的超时时间(例如 30 秒)。我们根本不希望 #2 出现整体超时,但我们确实希望如果字节在一段时间内停止写入,则上传失败。值得庆幸的是,我们已经解决了#2HttpWebRequest.ReadWriteTimeout
,我们只需要修复恼人的行为HttpWebRequest.Timeout
。事实证明,异步版本GetRequestStream
and GetResponse
做我们需要的事情。
所以你想要这段代码:
public static class AsyncExtensions
{
public static Task<T> WithTimeout<T>(this Task<T> task, TimeSpan timeout)
{
return Task.Factory.StartNew(() =>
{
var b = task.Wait((int)timeout.TotalMilliseconds);
if (b) return task.Result;
throw new WebException("The operation has timed out", WebExceptionStatus.Timeout);
});
}
}
而不是打电话GetRequestStream
and GetResponse
我们将调用异步版本:
var uploadStream = httpRequest.GetRequestStreamAsync().WithTimeout(TimeSpan.FromSeconds(30)).Result;
对于响应也类似:
var response = (HttpWebResponse)httpRequest.GetResponseAsync().WithTimeout(TimeSpan.FromSeconds(30)).Result;
这就是您所需要的。现在您的上传将更加可靠。您可以将整个上传包装在重试循环中以增加确定性。