【问题标题】:Request is not finished when using SqlDataReader in FileStreamResult using ASP.NET MVC使用 ASP.NET MVC 在 FileStreamResult 中使用 SqlDataReader 时请求未完成
【发布时间】:2015-10-19 01:32:22
【问题描述】:

出于性能考虑,我使用SqlConnectionSqlReaderStream 从SQL Server 数据库返回byte[]

private static SqlConnection GetConnection()
{
    var sqlConnectionStringBuilder =
        new SqlConnectionStringBuilder(
            ConfigurationManager.ConnectionStrings["StudentsSystemEntities"].ConnectionString)
            {
                Pooling = true
            };
    var connection = new SqlConnection(sqlConnectionStringBuilder.ConnectionString);
    connection.Open();
    return connection;
}

public FileDownloadModel GetFileById(Guid fileId)
{
    var connection = GetConnection();
    var command = new SqlCommand(
        @"SELECT [FileSize], [FileExtension], [Content] FROM [dbo].[Files] WHERE [FileId] = @fileId;",
        connection);
    var paramFilename = new SqlParameter(@"fileId", SqlDbType.UniqueIdentifier) { Value = fileId };
    command.Parameters.Add(paramFilename);

    var reader = command.ExecuteReader(
        CommandBehavior.SequentialAccess | CommandBehavior.SingleResult
        | CommandBehavior.SingleRow | CommandBehavior.CloseConnection);

    if (reader.Read() == false) return null;

    var file = new FileDownloadModel
                    {
                        FileSize = reader.GetInt32(0),
                        FileExtension = reader.GetString(1),
                        Content = new SqlReaderStream(reader, 2)
                    };
    return file;
}

我在 ASP.NET MVC 操作中使用了这个 GetFileById 方法:

[HttpGet]
public ActionResult Get(string id)
{
    // Validations omitted

    var file = this.filesRepository.GetFileById(guid);

    this.Response.Cache.SetCacheability(HttpCacheability.Public);
    this.Response.Cache.SetMaxAge(TimeSpan.FromDays(365));
    this.Response.Cache.SetSlidingExpiration(true);

    this.Response.AddHeader("Content-Length", file.FileSize.ToString());
    var contentType = MimeMapping.GetMimeMapping(
        string.Format("file.{0}", file.FileExtension));
    // this.Response.BufferOutput = false;
    return new FileStreamResult(file.Content, contentType);
}

我正在以下行中将 MVC FileStreamResultSqlReaderStream 连接起来:

return new FileStreamResult(file.Content, contentType);

当我尝试使用 Chrome(或 Firefox...)加载资源时加载了整个文件,但出现以下错误:

注意:请求尚未完成!

响应标头:

HTTP/1.1 200 OK
Cache-Control: public, max-age=31536000
Content-Length: 33429
Content-Type: image/png
Server: Microsoft-IIS/10.0
X-AspNetMvc-Version: 5.2
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?QzpcR---trimmed---G5n?=
X-Powered-By: ASP.NET
Date: Tue, 28 Jul 2015 13:02:55 GMT

其他信息:

  • 我没有使用任何 Chrome 扩展程序
  • 问题仅在于给定的Get 操作。所有其他操作均正常加载
  • FilesControllerGet 操作所在的位置)直接继承自 Controller
  • 文件加载成功但浏览器仍在等待服务器:
  • 我在使用 Firefox 时遇到的问题完全相同

问题的可能原因是什么?

SqlReaderStream 类的源代码

public class SqlReaderStream : Stream
{
    private readonly int columnIndex;

    private SqlDataReader reader;

    private long position;

    public SqlReaderStream(
        SqlDataReader reader, 
        int columnIndex)
    {
        this.reader = reader;
        this.columnIndex = columnIndex;
    }

    public override long Position
    {
        get { return this.position; }
        set { throw new NotImplementedException(); }
    }

    public override bool CanRead
    {
        get { return true; }
    }

    public override bool CanSeek
    {
        get { return false; }
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    public override long Length
    {
        get { throw new NotImplementedException(); }
    }

    public override void Flush()
    {
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        var bytesRead = this.reader.GetBytes(
            this.columnIndex, this.position, buffer, offset, count);
        this.position += bytesRead;
        return (int)bytesRead;
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        throw new NotImplementedException();
    }

    public override void SetLength(long value)
    {
        throw new NotImplementedException();
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        throw new NotImplementedException();
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing && null != this.reader)
        {
            this.reader.Dispose();
            this.reader = null;
        }

        base.Dispose(disposing);
    }
}

【问题讨论】:

  • 您能发布SqlReaderStream 的代码吗?问题可能在这个类中。
  • 当然:)。查看我编辑的帖子。
  • 你的[Content]字段真的包含33429字节的数据吗?或者它有更少?执行时得到什么值:reader.GetBytes(2, 0, null, 0, 0);?
  • 我认为FileStreamResult 不可能知道流已经结束。您没有返回Length,也没有EndOfStream 属性
  • 试试this.reader.GetBytes( this.columnIndex, offset, buffer, 0, count);

标签: c# asp.net-mvc http filestream sqlconnection


【解决方案1】:

真正的问题在于这一行:

this.Response.AddHeader("Content-Length", file.FileSize.ToString());

您提供的文件大小错误,大于实际文件大小。这就是浏览器等待其他内容的原因。

要快速检查这确实是问题所在,请将行修改为以下内容

this.Response.AddHeader("Content-Length", (file.FileSize/10).ToString());

这会将下载限制为所讨论文件的 1/10 - 但由于文件大小可能相差不到一个数量级, 这些文件将有足够的数据来提供所要求文件大小的 10%, 并且该问题将不再重现。

【讨论】:

  • 这就是问题所在!非常感谢。准确地说,我通过不向浏览器发送 Content-Length 标头来修复它(删除 this.Response.AddHeader("Content-Length", file.FileSize.ToString()); 行)
  • 我通常不会离开,谢谢.. 它只会让回复变得混乱,但我不得不破例。谢谢。
【解决方案2】:

我不会在FileStreamResult 中返回SqlReaderStream,因为流不是可搜索。我想这可能是个问题。

尝试将Stream 复制到MemoryStream字节数组 并在GetFileById 中返回。

此外,如果您在函数中本地读取SqlReaderStream,则可以关闭与数据库的连接。

return File(byteArray, contentType, name);

【讨论】:

  • 这个方案不适合大文件,这个方法我用了很长时间,但是现在我需要处理大文件,它不像以前那样工作(有时什么都没有发生:)
【解决方案3】:

此问题与Chrome浏览器有关,仅在第一个请求未完成时。尝试使用 Firefox 或 Safari。

有关详细信息,请参阅下面的链接。 http://www.codeproject.com/Questions/821716/PHP-serve-MP-Chrome-Provisional-headers-are-shown

"CAUTION: provisional headers are shown" in Chrome debugger

Chrome block requests

【讨论】:

  • 正如我在问题中提到的:The exact same problem I am having with Firefox
猜你喜欢
  • 2013-03-29
  • 1970-01-01
  • 1970-01-01
  • 2020-04-12
  • 1970-01-01
  • 2017-03-23
  • 1970-01-01
  • 1970-01-01
  • 2010-11-10
相关资源
最近更新 更多