本文可帮助你解决在使用类在运行 Microsoft .NET Framework 的计算机上发送大量数据时 HttpWebRequest
可能会引发错误的问题。
原始产品版本: .NET Framework
原始 KB 数: 908573
症状
使用HttpWebRequest
类发送大量数据POST
PUT
时,请求可能会在运行 .NET Framework 的计算机上失败。 此外,可能会收到内存不足或超时错误消息。
如果未收到内存不足或超时错误消息,你可能会注意到使用该 HttpWebRequest
类的应用程序使用大量内存。 使用性能监视器监视使用该HttpWebRequest
类的应用程序时,发送数据时,专用字节计数将继续增加。 因此,由于内存和资源利用率增加,计算机和其他应用程序中的性能也可能会降低。
注释
默认情况下可以上传的数据量将因计算机上的可用内存和资源而异。
原因
之所以出现此问题,是因为使用类时 HttpWebRequest
,.NET Framework 会默认缓冲传出数据。
解决方法
若要解决此问题,请将 HttpWebRequest.AllowWriteStreamBuffering
属性设置为 false。
解决方法导致的错误
将属性设置为 false 时,可能会收到类似于以下示例的 HttpWebRequest.AllowWriteStreamBuffering
错误消息:
此请求需要缓冲数据,以便对重定向进行身份验证才能成功。
若要在属性设置为 false 时POST
使用PUT
或HttpWebRequest.AllowWriteStreamBuffering
请求成功发送大量数据,请使用以下方法之一,具体取决于要使用的身份验证方法。
匿名身份验证
如果 Web 服务器配置为使用匿名身份验证,请将 HttpWebRequest.AllowWriteStreamBuffering
属性设置为 false。 无需进行其他更改。
基本身份验证
如果 Internet Information Services (IIS) Web 服务器配置为使用基本身份验证,并且可以将属性设置为 HttpWebRequest.AllowWriteStreamBuffering
false,则必须在发送HEAD
或POST
请求之前发送请求PUT
以预先对连接进行身份验证。 还应将 HttpWebRequest.PreAuthenticate
属性设置为 true。 然后发送 POST
或 PUT
请求,然后接收响应。 若要执行此操作,请使用类似于以下代码示例的代码。
public void test(Uri URL)
{
HttpWebRequest WRequest;
HttpWebResponse WResponse;
//preAuth the request
// You can add logic so that you only pre-authenticate the very first request.
// You should not have to pre-authenticate each request.
WRequest = (HttpWebRequest)HttpWebRequest.Create(URL);
// Set the username and the password.
WRequest.Credentials = new NetworkCredential(user, password);
WRequest.PreAuthenticate = true;
WRequest.UserAgent = "Upload Test";
WRequest.Method = "HEAD";
WRequest.Timeout = 10000;
WResponse = (HttpWebResponse)WRequest.GetResponse();
WResponse.Close();
// Make the real request.
WRequest = (HttpWebRequest)HttpWebRequest.Create(URL);
// Set the username and the password.
WRequest.Credentials = new NetworkCredential(user, password);
WRequest.PreAuthenticate = true;
WRequest.UserAgent = "Upload Test";
WRequest.Method = "POST";
WRequest.AllowWriteStreamBuffering = false;
WRequest.Timeout = 10000;
FileStream ReadIn = new FileStream("c:\\testuploadfile.txt", FileMode.Open, FileAccess.Read);
ReadIn.Seek(0, SeekOrigin.Begin); // Move to the start of the file.
WRequest.ContentLength = ReadIn.Length; // Set the content length header to the size of the file.
Byte[] FileData = new Byte[ReadIn.Length]; // Read the file in 2 KB segments.
int DataRead = 0;
Stream tempStream = WRequest.GetRequestStream();
do
{
DataRead = ReadIn.Read(FileData,0,2048);
if (DataRead > 0) //we have data
{
tempStream.Write(FileData,0,DataRead);
Array.Clear(FileData,0, 2048); // Clear the array.
}
} while (DataRead > 0);
WResponse = (HttpWebResponse)WRequest.GetResponse();
// Read your response data here.
// Close all streams.
ReadIn.Close();
tempStream.Close();
WResponse.Close();
}
注释
根据应用程序的设计方式,可能不必通过发送 HEAD
请求来预验证每个请求。
Windows 集成身份验证
可以使用 Negotiate 或 Windows 质询/响应(NTLM)Windows 身份验证配置安装 IIS 以响应的计算机。 如果 IIS 配置为使用 Negotiate for Windows 身份验证,则客户端可以使用 Kerberos 或 NTLM 进行身份验证。 如果 IIS 配置为使用 NTLM 身份验证,则只能使用 NTLM 身份验证,并且不支持 Kerberos 身份验证。
如果使用 Kerberos 身份验证协商,请使用以下解决方法。 如果使用 NTLM,解决方法将失败。
与 Kerberos 身份验证协商
如果 IIS Web 服务器配置为使用 Negotiate 身份验证,并且必须将属性设置为 HttpWebRequest.AllowWriteStreamBuffering
false,则必须发送 HEAD 请求,以便在发送 POST 或 PUT 请求之前预先对连接进行身份验证。 还可以将 HttpWebRequest.PreAuthenticate
属性设置为 true。 此外,可能需要将 HttpWebRequest.UnsafeAuthenticatedConnectionSharing
属性设置为 true。 然后,发送 POST 或 PUT 请求,然后接收响应。 为此,可以使用类似于以下代码示例的代码。
注释
如果客户端无法使用 Kerberos 进行协商身份验证,则此解决方法将失败。 还必须确保属性 HttpWebRequest.KeepAlive
设置为 true。 默认情况下,属性 HttpWebRequest.KeepAlive
的设置为 true。 Kerberos 和基本身份验证的逻辑几乎相同。
public void test(Uri URL)
{
HttpWebRequest WRequest;
HttpWebResponse WResponse;
CredentialCache myCredCache = new CredentialCache();
myCredCache.Add(URL,"Negotiate",(NetworkCredential) CredentialCache.DefaultCredentials);
// Pre-authenticate the request.
WRequest = (HttpWebRequest)HttpWebRequest.Create(URL);
// Set the username and the password.
WRequest.Credentials = myCredCache;
// This property must be set to true for Kerberos authentication.
WRequest.PreAuthenticate = true;
// Keep the connection alive.
WRequest.UnsafeAuthenticatedConnectionSharing = true;
WRequest.UserAgent = "Upload Test";
WRequest.Method = "HEAD";
WRequest.Timeout = 10000;
WResponse = (HttpWebResponse)WRequest.GetResponse();
WResponse.Close();
// Make the real request.
WRequest = (HttpWebRequest)HttpWebRequest.Create(URL);
// Set the username and the password.
WRequest.Credentials = myCredCache;
// This property must be set to true for Kerberos authentication.
WRequest.PreAuthenticate = true;
// Keep the connection alive.
WRequest.UnsafeAuthenticatedConnectionSharing = true;
WRequest.UserAgent = "Upload Test";
WRequest.Method = "POST";
WRequest.AllowWriteStreamBuffering = false;
WRequest.Timeout = 10000;
FileStream ReadIn = new FileStream("c:\\testuploadfile.txt ", FileMode.Open, FileAccess.Read);
ReadIn.Seek(0, SeekOrigin.Begin); // Move to the start of the file.
WRequest.ContentLength = ReadIn.Length; // Set the content length header to the size of the file.
Byte[] FileData = new Byte[ReadIn.Length]; // Read the file in 2 KB segments.
int DataRead = 0;
Stream tempStream = WRequest.GetRequestStream();
do
{
DataRead = ReadIn.Read(FileData,0,2048);
if (DataRead > 0) // We have data.
{
tempStream.Write(FileData,0,DataRead);
Array.Clear(FileData,0, 2048); // Clear the array.
}
}while(DataRead > 0);
WResponse = (HttpWebResponse)WRequest.GetResponse();
// Read your response data here.
// Close all streams
ReadIn.Close();
tempStream.Close();
WResponse.Close();
}
注释
根据应用程序的设计方式,可能不必通过发送 HEAD 请求来预验证每个请求。
NTLM 身份验证
如果 IIS Web 服务器还配置为将 NTLM 身份验证与 Windows 集成身份验证配合使用,并且必须将属性设置为 false,则可以在客户端代码中将 HttpWebRequest.AllowWriteStreamBuffering
身份验证类型设置为 NTLM。 将 IIS 配置为同时使用 Negotiate 和 NTLM 身份验证,并将身份验证类型设置为客户端代码中的 NTLM 后,可以通过将 IIS 元数据库中的属性设置为 AuthPersistSingleRequest
false 来配置 IIS 如何处理身份验证请求。
注释
有关如何配置 IIS 以支持 Negotiate 和 NTLM 身份验证的详细信息,请参阅“ 参考” 部分。
在发送HEAD
请求并将属性设置为 POST
true 之前,还必须发送HttpWebrequest.UnsafeAuthenticatedConnectionSharing
请求以预先对连接进行身份验证。 然后,将 HttpWebRequest.PreAuthenticate
属性设置为 false。 最后,发送 POST
或 PUT
请求,然后接收响应。 为此,请使用类似于以下代码示例的代码。
public void test(Uri URL)
{
HttpWebRequest WRequest;
HttpWebResponse WResponse;
CredentialCache myCredCache = new CredentialCache();
myCredCache.Add(URL,"NTLM",(NetworkCredential) CredentialCache.DefaultCredentials);
// Pre-authenticate the request.
WRequest = (HttpWebRequest)HttpWebRequest.Create(URL);
// Set the username and the password.
WRequest.Credentials = myCredCache;
// For NTLM authentication, you must set the following property to true
// so the connection does not close.
WRequest.UnsafeAuthenticatedConnectionSharing = true;
WRequest.UserAgent = "Upload Test";
WRequest.Method = "HEAD";
WRequest.Timeout = 10000;
WResponse = (HttpWebResponse)WRequest.GetResponse();
WResponse.Close();
// Make the real request.
WRequest = (HttpWebRequest)HttpWebRequest.Create(URL);
// Set the username and the password.
WRequest.Credentials = myCredCache;
// For NTLM authentication, you must set the following property to true
// so the connection does not close.
WRequest.UnsafeAuthenticatedConnectionSharing = true;
WRequest.UserAgent = "Upload Test";
WRequest.Method = "POST";
WRequest.AllowWriteStreamBuffering = false;
WRequest.Timeout = 10000;
FileStream ReadIn = new FileStream("c:\\ testuploadfile.txt", FileMode.Open, FileAccess.Read);
ReadIn.Seek(0, SeekOrigin.Begin); // Move to the start of the file.
WRequest.ContentLength = ReadIn.Length; // Set the content length header to the size of the file.
Byte[] FileData = new Byte[ReadIn.Length]; // Read the file in 2 KB segments.
int DataRead = 0;
Stream tempStream = WRequest.GetRequestStream();
do
{
DataRead = ReadIn.Read(FileData,0,2048);
if (DataRead > 0) // We have data.
{
tempStream.Write(FileData,0,DataRead);
Array.Clear(FileData,0, 2048); // Clear the array.
}
}while(DataRead > 0);
WResponse = (HttpWebResponse)WRequest.GetResponse();
// Read your response data here.
// Close all streams.
ReadIn.Close();
tempStream.Close();
WResponse.Close();
}
注释
根据应用程序的设计方式,可能不必通过发送 HEAD
请求来预验证每个请求。