将文件加载到位图但保持原始文件不变

如何在C#中做到这一点?

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

如果我使用Bitmap.FromStream(),原始文件不会被锁定,但文档说“你必须在图像的生命周期内保持流打开”。 这可能意味着文件仍然链接到图像对象(例如,如果文件更改,则对象反之亦然)。

我想要做的只是读取位图并将其保存到一个对象,然后在文件和Image对象之间没有任何链接

有关此行为的一些背景信息:位图使用内存映射文件来访问位图中的像素。 这是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,如果您使用位图,则很难诊断出“一般错误”。 由Image类引起的懒惰读取流。

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

为了在不锁定文件的情况下创建图像,您必须创建图像的FileStream的副本。 检查此页面在两个Stream实例之间复制的最佳方法 – C#以了解如何复制流。

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

我已经使用这种复制到MemoryStream的技术,然后将MemoryStream很多次送到Bitmap.FromStream。 然而,这种技术也有一个问题。

如果您计划稍后在加载的图像上使用其中一个Bitmap.Save方法,那么您将必须保持流存活(即,在图像加载后不处理它),否则您将获得可怕的“发生了一般GDI +错误“exception!