使用 ASP.NET MVC 在 FileStreamResult 中使用 SqlDataReader 时请求未完成
Request is not finished when using SqlDataReader in FileStreamResult using ASP.NET MVC
出于性能考虑,我使用 SqlConnection
和 SqlReaderStream
从 SQL 服务器数据库 返回 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 FileStreamResult
与 SqlReaderStream
连接:
return new FileStreamResult(file.Content, contentType);
当我尝试使用 Chrome(或 Firefox...)加载资源时 整个文件都已加载,但出现以下错误:
CAUTION: request is not finished yet!
回复headers:
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
操作上。所有其他操作正在正常加载
FilesController
(其中Get
动作)直接继承自Controller
class
- 文件加载成功但浏览器仍在等待服务器:
- 我在使用 Firefox 时遇到的问题完全相同
问题的可能原因是什么?
源代码 SqlReaderStream
class
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);
}
}
此问题与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
我不会在 FileStreamResult
中 return SqlReaderStream
,因为流 不可搜索 。我想这可能是个问题。
尝试将 Stream
复制到 MemoryStream
或 字节数组 并将其 return 复制到 GetFileById
中。
此外,如果您在本地读取 SqlReaderStream
函数,您可以关闭与数据库的连接。
return File(byteArray, contentType, name);
真正的问题在于这一行:
this.Response.AddHeader("Content-Length", file.FileSize.ToString());
您提供的文件大小错误,它比实际文件大小大。这就是浏览器等待额外内容的原因。
要快速检查这确实是问题所在,请将行修改为以下内容
this.Response.AddHeader("Content-Length", (file.FileSize/10).ToString());
这会将下载限制为相关文件的 1/10 - 但由于文件大小可能相差不到一个数量级,
这些文件将有足够的数据来提供所要求文件大小的 10%,
并且该问题将不再重现。
出于性能考虑,我使用 SqlConnection
和 SqlReaderStream
从 SQL 服务器数据库 返回 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 FileStreamResult
与 SqlReaderStream
连接:
return new FileStreamResult(file.Content, contentType);
当我尝试使用 Chrome(或 Firefox...)加载资源时 整个文件都已加载,但出现以下错误:
CAUTION: request is not finished yet!
回复headers:
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
操作上。所有其他操作正在正常加载 FilesController
(其中Get
动作)直接继承自Controller
class- 文件加载成功但浏览器仍在等待服务器:
- 我在使用 Firefox 时遇到的问题完全相同
问题的可能原因是什么?
源代码 SqlReaderStream
class
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);
}
}
此问题与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
我不会在 FileStreamResult
中 return SqlReaderStream
,因为流 不可搜索 。我想这可能是个问题。
尝试将 Stream
复制到 MemoryStream
或 字节数组 并将其 return 复制到 GetFileById
中。
此外,如果您在本地读取 SqlReaderStream
函数,您可以关闭与数据库的连接。
return File(byteArray, contentType, name);
真正的问题在于这一行:
this.Response.AddHeader("Content-Length", file.FileSize.ToString());
您提供的文件大小错误,它比实际文件大小大。这就是浏览器等待额外内容的原因。
要快速检查这确实是问题所在,请将行修改为以下内容
this.Response.AddHeader("Content-Length", (file.FileSize/10).ToString());
这会将下载限制为相关文件的 1/10 - 但由于文件大小可能相差不到一个数量级, 这些文件将有足够的数据来提供所要求文件大小的 10%, 并且该问题将不再重现。