在.NET环境下处理图像时,可能会遇到一些资源管理的问题。例如,如果一个Image对象是从文件创建的,那么该文件在对象的生命周期内会被锁定,这意味着在应用程序中创建的临时图像文件在图像完全释放之前不能被删除。本文将介绍SafeImage类,它能够解决这些问题。
SafeImage类通过复制Stream来创建Image,然后同时维护并释放对复制Stream和Image的引用。这样,即使原始Stream被释放,Image对象也能正常工作。但是,仍然需要正确地释放原始Stream。
另一种可能的解决方案是创建Image的副本并使用它,而不是使用有问题的那个。这是一个可靠且有效的解决方案,只要在复制后立即释放原始图像。但是,复制图像可能会很慢,如果频繁这样做,可能会影响应用程序的性能。SafeImage类可以选择性地使用这种方法。
SafeImage类提供了一个UseStreamType枚举,允许指定保护Image完整性的算法。可能的值包括:
SafeImage基本上可以作为Image的替代品使用。这是通过定义隐式操作符将SafeImage转换为或从Image实现的。这意味着像以下这样的代码:
C#
SafeImage img = new SafeImage(filename);
pictureBox1.Image = img;
将会正常工作。返回的图像实际上是正在保存的副本,因为SafeImage无法知道接收者可能会对它做什么(例如,释放它可能会很糟糕)。
创建SafeImage的所有工作都是在各种构造函数中完成的。静态FromXxxx方法只是调用具有匹配签名的构造函数。以下是每个构造函数的描述:
C#
SafeImage(Image image, UseStreamType streamType = UseStreamType.None);
复制提供的Image。默认情况下,此方法使用Bitmap.FromImage方法创建新的Image。但是,如果传递了UseStreamType.MemoryStream、UseStreamType.TempFileStream或UseStreamType.BestFit选项之一,此构造函数将创建适当的Stream,将原始Image保存到其中,然后从新创建的Stream创建新的Image。SafeImage.FromBitmap方法调用此构造函数以创建其返回值。
C#
SafeImage(Stream stream, UseStreamType streamType = UseStreamType.BestFit);
SafeImage(Stream stream, bool useEmbeddedColorManagement, UseStreamType streamType = UseStreamType.BestFit);
SafeImage(Stream stream, bool useEmbeddedColorManagement, bool validateImageData, UseStreamType streamType = UseStreamType.BestFit);
上述构造函数中的每一个都创建了适当类型的新Stream(MemoryStream或FileStream),并将原始Stream的内容复制到新Stream中。然后使用Image.FromStream方法(具有相应的签名)从副本Stream创建Image。SafeImage中保留了对副本Stream的引用,直到SafeImage被释放。
如果这些构造函数传递了UseStreamType.None选项,那么将直接从输入Stream创建一个临时Image。然后通过复制临时Image创建新的Image,最后释放临时Image。代码如下:
C#
using (Image temp = Image.FromStream(stream)) {
_image = new Bitmap(_image);
}
C#
SafeImage(string filename, UseStreamType streamType = UseStreamType.BestFit);
SafeImage(string filename, bool useEmbeddedColorManagement, UseStreamType streamType = UseStreamType.BestFit);
上述构造函数通过打开文件,将内容复制到新的MemoryStream或FileStream(视情况而定)。然后关闭中间FileStream,并从副本数据创建Image。这样做可以释放文件上的锁,使应用程序能够执行其他操作。
与Stream类型构造函数一样,如果这些构造函数传递了UseStreamType.None值,那么将从打开的FileStream创建一个临时Image,然后进行复制。然后释放FileStream和临时Image。
C#
// 下载图像
SafeImage image;
using (HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://someImageUrl")) {
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) {
using (Stream responseStream = response.GetResponseStream()) {
image = new SafeImage(responseStream);
}
}
}
C#
// 从数据库BLOB获取图像
DbConnection connection;
// ... 设置连接
List images = new List();
using (DbCommand cmd = connection.CreateCommand()) {
cmd.CommandText = "SELECT Image FROM Images WHERE Image IS NOT NULL";
using (DbDataReader rs = cmd.ExecuteReader()) {
while (rs.Read()) {
images.Add(new SafeImage(rs.GetStream(0)));
}
}
}
C#
// 将SafeImage分配给PictureBox
string tempImage = "myFile.jpg";
pictureBox1.Image = new SafeImage(tempImage);
File.Delete(tempImage);
// 成功