5 asp.net asp.net-mvc odata pushstreamcontent asp.net-web-api2
我需要使用Odata协议通过WebAPI批量导出内容。我们正在尝试使用PushStreamContent将结果直接从数据库中流式传输。当我在本地IIS实例中运行该服务时,它运行良好,但是当我将该服务推送到服务器时,它将流传输数据,并在最后2 KB处暂停和“挂起”。
我通过跟踪文件大小来验证这一点。在本地运行时,我将得到一个9094KB的文件,当我将相同的代码部署到服务器上时,我将获得9092KB,然后连接保持打开状态并停止传输。如果我杀死客户端并查看文件,我将看到在写入过程中流被截断的json。此外,我可以查看IIS中打开的连接,并看到该连接仍处于活动状态。
无论如何,为什么PushStreamContent似乎只是停止发送数据而不关闭流?如果发生错误,则流和连接将关闭。
public HttpResponseMessage GetBulkExport(ODataQueryOptions<vwBulkExport> options)
{
var reportData = options.ApplyTo(dbContext.vwBulkExport, new ODataQuerySettings() { EnsureStableOrdering = false });
return new ResponseStreamer(Request).StreamAsync(reportData);
}
public class ResponseStreamer
{
private HttpRequestMessage request;
public ResponseStreamer(HttpRequestMessage request)
{
this.request = request;
}
public HttpResponseMessage StreamAsync(IQueryable data)
{
HttpResponseMessage response = request.CreateResponse();
response.Content = new PushStreamContent(
async (outputStream, httpContent, transportContext) =>
{
try
{
int counter = 0;
foreach (var item in data)
{
counter++;
string json = JsonConvert.SerializeObject(item);
var buffer = Encoding.UTF8.GetBytes(json);
await outputStream.WriteAsync(buffer, 0, buffer.Length);
if (counter == 10)
{
counter = 0;
await outputStream.FlushAsync();
}
}
}
finally
{
await outputStream.FlushAsync();
outputStream.Close();
outputStream.Dispose();
}
});
return response;
}
}
Run Code Online (Sandbox Code Playgroud)
这是我的客户代码
using (var writer = File.OpenWrite("C:\\temp\\" + Guid.NewGuid().ToString()))
{
var client = new RestClient("http://localhost");
var url = "/odata/BulkExport";
var request = new RestRequest(url);
request.AddHeader("authorization", string.Format("Bearer {0}", authToken));
request.ResponseWriter = (responseStream) => responseStream.CopyTo(writer);
var response = client.DownloadData(request);
}
Run Code Online (Sandbox Code Playgroud)
我正在做广泛的测试,我认为正在发生的事情是流永远不会关闭(因此,最后一个块永远不会发送出去),我通过将上面的数据迭代更改为此得出了这个结论:
for (int count = 0; count < 1000; count++) //foreach (var item in data)
{
string json = JsonConvert.SerializeObject(count.ToString()) + Environment.NewLine;
var buffer = Encoding.Default.GetBytes(json);
await outputStream.WriteAsync(buffer, 0, buffer.Length);
}
Run Code Online (Sandbox Code Playgroud)
我看到的是,它只会返回600个“行”。再次似乎缺少2 KB。然后,我将循环更改为,count <601并传输了整个流,但是该流永远不会关闭。我认为正在发生的事情是内部缓冲区的大小,如果大约在4K左右(这是打印出来的数字0-600的数字),并且由于流没有关闭,则最后几个字节将永远不会被接收到。这有意义吗?
无论如何,为什么流不会关闭?我终于有了它,而且看不到任何错误。
我发现了更多信息。HTTP 1.1规范说,分块的流必须以零长度的块结尾。经过一番挖掘,我发现它应该发生,但是无论出于什么原因都没有。在我的客户端中,我删除了Connection: Keep-Alive标头并用Connection: Close同样的问题替换了标头,但是当我强制关闭连接(通过关闭测试应用程序)后,最后几个字节写入磁盘,一切都很好。这就是我知道零长度块未发送的方式。
所以现在问题就成了。当关闭流时,为什么最终的零长度块未发送?根据我的阅读,调用HttpContext.Current.ApplicationInstance.CompleteRequest();应强制请求结束并写出该块。我将其添加为我的finally块的最后一行,并使用调试器知道它正在运行。但是,该块仍未设置。
记住,所有这些都可以在IIS托管的我的开发机上工作,而不能在Web服务器上工作。
我的机器运行的是Windows 10,我安装了asp.net 5,并使用所有默认设置的IIS7。
Web服务器是Windows Server(我不确定版本),但运行IIS8,但只安装了asp.net 4.5。我最初的预感是这是问题所在-不同版本的asp.net框架,但是我检查了一下,该项目仅针对ASP.NET 4.5。我仍将尝试更新服务器,但是由于我的目标是4.5,所以我认为它不会有任何好处。
小智 2
我认为该问题是由于 .net 框架中的错误造成的。将服务器更新到 .net 4.6 后,它可以正常工作。
这是我应用的补丁。https://www.microsoft.com/en-us/download/details.aspx?id=48137
| 归档时间: |
|
| 查看次数: |
984 次 |
| 最近记录: |