我遇到了通过Linq同时加载400多张图片的问题。
所有图像都以二进制形式存储在数据库中(无需讨论)。
要检索它们,我会提取唯一ID并将其发送到浏览器。 我有一个.ashx处理程序,它接收ID并将图像作为jpeg-data返回。
所以在标记中我只是把
<img id="img" src='/GetWebImage.ashx?id=<%= Model.Id %>' />
这引起了一些问题,因为当我尝试在SQL服务器squirks时加载400+时。挂钩Fiddler我可以看到SQL服务器在大部分图像上返回404,错误如下:
ExecuteReader需要一个开放且可用的连接。连接的当前状态是连接。
或
已经有一个与此命令关联的打开DataReader,必须先关闭
或
读取器关闭时调用Read的尝试无效
所以我猜测sql server无法一次处理所有的调用。
有人可以帮我正确地做到这一点吗?
我需要在一次调用中从数据库加载400多个图像。我可以加载图像(而不仅仅是ID,并通过模型解析,我将如何从二进制System.Drawing.Image转到-tag中的路径?
另一个解决方案是逐个加载图像jQuery(并等待加载图像)然后将它们插入到正确的地方 - 但这似乎是一种非常糟糕的方法。所以我猜我从一开始就做错了。
感谢您提出的好建议。我发现我的处理程序实际上使用GDS缩放每个图像。不这样做,使得处理程序更快地服务于图像而不会失败。所以我猜SQL Server问题实际上是由.net无法足够快地扩展引起的。 我有点困惑,因为我有另一项服务以相同的方式提供10.000+图像,没有任何问题。
但我肯定会去一些缓存,因为每张图片只有一个唯一的URL。 有关缓存的任何建议吗?
对于包含许多图像的未来项目,我将用于存储图像的路径并将其保存在文件系统中。然后IIS将是服务图像的限制。
答案 0 :(得分:2)
你应该真的使用缓存来做这种事情。
答案 1 :(得分:1)
对于缓存,只需将此代码添加到您的处理程序:
Response.Cache.SetCacheability(HttpCacheability.Public);
答案 2 :(得分:0)
答案 3 :(得分:0)
为避免大图像或多次调用DB的内存不足异常,请使用以下代码对图像进行流式处理:
/// <summary>
/// Writes the binary data at the specified data record index to a stream using a buffer.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="stream">The stream to write to.</param>
/// <param name="index">The index in the data record to read from.</param>
/// <remarks>
/// The data is read sequentially.
/// </remarks>
public static void WriteToStream(IQueryable<Binary> source, Stream stream, int index)
{
var cmd = DbContext.GetCommand(source);
cmd.Connection.Open();
try
{
using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
{
while (reader.Read())
{
using (var recordStream = new DataRecordStream(reader, index))
recordStream.CopyTo(stream);
}
}
}
finally
{
cmd.Connection.Close();
}
}
这是DataRecordStream类,它包装了一个dataReader并使其行为像一个流:
public class DataRecordStream : Stream
{
#region Fields
private long _position;
private bool _isOpen;
#endregion
#region Properties
public override bool CanRead
{
get { return _isOpen; }
}
public override bool CanSeek
{
get { return false; }
}
public override bool CanWrite
{
get { return false; }
}
public IDataRecord DataRecord { get; private set; }
public int Index { get; private set; }
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="DataRecordStream"/> class.
/// </summary>
/// <param name="dataRecord">The data record.</param>
/// <param name="index">The index in the data record to read from.</param>
public DataRecordStream(IDataRecord dataRecord, int index)
{
DataRecord = dataRecord;
Index = index;
_position = 0;
_isOpen = true;
}
#endregion
#region Functions
public override void Flush()
{
}
public override long Length
{
get { throw new NotSupportedException(); }
}
public override long Position
{
get
{
return _position;
}
set
{
throw new NotSupportedException();
}
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
_isOpen = false;
}
public override int Read(byte[] buffer, int offset, int count)
{
if (!_isOpen)
throw new ObjectDisposedException("Stream is closed");
int bytesRead = (int)DataRecord.GetBytes(Index, _position, buffer, offset, count);
_position += bytesRead;
return bytesRead;
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
#endregion