2010-08-02 66 views
7

如何在C#中执行此操作?将文件加载到位图,但保留原始文件不变

如果我使用Bitmap.FromFile(),原始文件被锁定。

如果我使用Bitmap.FromStream(),原始文件未被锁定,但文档中显示“您必须保持流在图像的整个生命周期内都处于打开状态”。这可能意味着该文件仍然与图像对象相关联(例如,如果文件改变了,对象反之亦然)。

我想要做的就是阅读的位图,并将其保存到对象后,有文件和图像目标之间没有任何联系

回答

25

有关此行为的一些背景信息:Bitmap使用内存映射文件访问位图中的像素。这是Windows API中非常基本的工具,它可以非常有效地将内存映射到文件数据。仅当程序读取内存时才从文件读取数据,虚拟内存页面不会在Windows分页文件中占用任何空间。

完全相同的机制用于加载.NET程序集。内存映射会对文件进行锁定。这基本上是为什么在.NET程序中使用程序集时它们被锁定的原因。 Image.Dispose()方法释放锁。对抗锁通常意味着你忘记处理你的位图。非常重要的是,忘记调用Dispose()通常不会导致.NET类的问题,除了Bitmap,因为它可能需要很多(非托管)内存。

是的,FromStream()阻止类进行优化。成本很高,在加载位图时需要加倍的内存。当位图很大时,这是一个问题,当程序运行了一段时间(碎片化地址空间)并且它不在64位操作系统上运行时,您正在避开OOM。绝对避免这样做,如果位图的宽度x高度x 4> = 45 MB,给予或采取。

一些代码,你不必通过CopyStream箍跳:

public static Image LoadImageNoLock(string path) { 
     var ms = new MemoryStream(File.ReadAllBytes(path)); // Don't use using!! 
     return Image.FromStream(ms); 
    } 

注意,你不想处置的MemoryStream,你会得到一个难以诊断的“一般错误”当位图得到使用时,如果你这样做。由懒惰阅读流的图像类引起的。

+0

你也需要这个来获取位图。位图位图=新的位图(LoadImageNoLock(路径)); – Harris 2015-01-28 09:13:38

+2

MemoryStream是一次性的,谁会照顾在此解决方案中处理? – kwesolowski 2015-02-04 09:48:07

4

通过从FileStream将其复制文件读入到内存变成MemoryStream。 (在堆栈溢出中搜索CopyStream以查找大量如何安全执行的示例。基本上在读取时循环,将每个块写入内存流,直到没有更多数据可读。)然后倒回MemoryStream(设置为Position = 0)和然后将其传递给Bitmap.FromStream

4

为了在不锁定文件的情况下创建图像,您必须创建图像的FileStream副本。 查看此页面Best way to copy between two Stream instances - C#了解如何复制流。

之后,只需从复制的流创建图像,然后就可以开始了。

+0

+1为我们寻找乔恩建议我们应该做的事情......你找到了一个非常好的职位。 – awe 2010-08-02 11:05:13

0

我已经使用这种技术复制到MemoryStream,然后将MemoryStream提供给Bitmap.FromStream很多次。但是,这种技术也有一个问题。

如果您计划稍后在加载的图像上使用其中一种Bitmap.Save方法,则必须保持该流处于活动状态(即,,不会在图像加载后进行处理),否则您将遇到可怕的“出现通用GDI +错误”异常!

相关问题