asp.net – PushStreamContent没有发送最终的零长度块来表示流的结束

我需要使用Odata协议通过WebAPI批量导出内容.我们正在尝试使用PushStreamContent直接将结果流出数据库.当我在本地IIS实例中运行该服务时,它工作得很好,但当我将其推送到服务器时,它将流式传输数据并暂停并在最后2千字节“挂起”.

我通过跟踪文件大小来验证.在本地运行中,我将获得一个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;
    }

}

这是我的客户端代码

    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);
    }

更新

我已经做了大量的测试,而我认为正在发生的是流永远不会被关闭(因此最后一个块永远不会被发送)我通过将上面的数据迭代更改为此来得出这个结论:

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);
}

我所看到的是它只返回600“行”.似乎还缺少2 KB.然后我将循环更改为计数< 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,我认为它不会有任何好处.

最佳答案 我认为问题是由.net框架中的错误引起的.将服务器更新到.net 4.6后,它正在运行.

这是我应用的补丁. https://www.microsoft.com/en-us/download/details.aspx?id=48137

点赞