使用 HttpWebRequest 类发送大量数据时,POST 或 PUT 请求失败
本文可帮助你解决在使用 HttpWebRequest
类在运行 Microsoft .NET Framework的计算机上发送大量数据时可能会引发错误的问题。
原始产品版本:.NET Framework
原始 KB 编号: 908573
症状
当你使用 HttpWebRequest
类通过 POST
或 PUT
请求发送大量数据时,请求可能会在运行 .NET Framework 的计算机上失败。 此外,还可能会收到内存不足或超时错误消息。
如果未收到内存不足或超时错误消息,你可能会注意到使用 HttpWebRequest
类的应用程序会占用大量内存。 使用 性能监视器 监视使用 HttpWebRequest
类的应用程序时,随着数据的发送,专用字节数将继续增加。 因此,由于内存和资源利用率增加,计算机和其他应用程序中的性能也可能会降低。
备注
默认情况下,可以上传的数据量会因计算机上的内存和资源而异。
原因
出现此问题的原因是,使用 HttpWebRequest
类时,默认情况下,.NET Framework会缓冲传出数据。
解决方法
若要解决此问题,请将 HttpWebRequest.AllowWriteStreamBuffering
属性设置为 false。
解决方法导致的错误
将 属性设置为 HttpWebRequest.AllowWriteStreamBuffering
false 时,可能会收到类似于以下示例的错误消息:
此请求需要缓冲数据才能成功进行身份验证重定向。
若要在属性设置为 false 时HttpWebRequest.AllowWriteStreamBuffering
使用 POST
或 PUT
请求成功发送大量数据,请使用以下方法之一,具体取决于要使用的身份验证方法之一。
匿名身份验证
如果 Web 服务器配置为使用匿名身份验证,请将 HttpWebRequest.AllowWriteStreamBuffering
属性设置为 false。 无需进行其他更改。
基本身份验证
如果 Internet Information Services (IIS) Web 服务器配置为使用基本身份验证,并且你可以将 属性设置为 HttpWebRequest.AllowWriteStreamBuffering
false,则必须在发送 POST
或 PUT
请求之前发送HEAD
对连接进行预身份验证的请求。 还应将 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 服务器配置为使用协商身份验证,并且必须将 属性设置为 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-Integrated 身份验证,并且必须将 属性设置为 HttpWebRequest.AllowWriteStreamBuffering
false,则可以在客户端代码中将身份验证类型设置为 NTLM。 将 IIS 配置为同时使用 Negotiate 和 NTLM 身份验证并在客户端代码中将身份验证类型设置为 NTLM 后,可以通过将 IIS 元数据库中的 AuthPersistSingleRequest
属性设置为 false 来配置 IIS 处理身份验证请求的方式。
备注
有关如何配置 IIS 以支持 Negotiate 和 NTLM 身份验证的详细信息,请参阅 参考 部分。
在发送请求并将 属性设置为 HttpWebrequest.UnsafeAuthenticatedConnectionSharing
true 之前,还必须发送HEAD
POST
请求以对连接进行预身份验证。 然后,将 属性 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
请求来预验证每个请求。