本文可帮助你解决在使用类在运行 Microsoft .NET Framework 的计算机上发送大量数据时 HttpWebRequest 可能会引发错误的问题。
原始产品版本: .NET Framework
原始 KB 数: 908573
症状
使用HttpWebRequest类发送大量数据POSTPUT时,请求可能会在运行 .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 请求来预验证每个请求。