如何在C#中执行此操作?
如果我使用Bitmap.FromFile(),原始文件将被锁定。
如果我使用Bitmap.FromStream(),原始文件不会被锁定,但文档说“你必须在图像的生命周期内保持流打开”。这可能意味着文件仍然链接到图像对象(例如,如果文件更改,则对象反之亦然)。
我想要做的只是读取位图并将其保存到对象中,之后文件和Image对象之间没有任何链接
答案 0 :(得分:26)
此行为的一些背景信息:位图使用内存映射文件来访问位图中的像素。这是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类引起的懒惰读取流。
答案 1 :(得分:4)
将文件从FileStream
复制到MemoryStream
,将文件读入内存。 (在Stack Overflow中搜索CopyStream
以找到大量如何安全地执行此操作的示例。基本上在读取时循环,将每个块写入内存流,直到没有更多数据要读取。)然后回退{{ 1}}(设置MemoryStream
),然后将其传递给Position = 0
。
答案 2 :(得分:4)
为了在不锁定文件的情况下创建图像,您必须创建图像的FileStream的副本。 请查看此页Best way to copy between two Stream instances - C#以了解如何复制流。
之后只需从复制的流中创建您的图像,您就可以开始使用了。
答案 3 :(得分:0)
我已经使用这种复制到MemoryStream的技术,然后将MemoryStream很多次送到Bitmap.FromStream。然而,这种技术也有一个问题。
如果您计划稍后在加载的图像上使用其中一个Bitmap.Save方法,那么您必须保持流存活(即,在加载图像后不将其丢弃),否则您将获得可怕的“一般GDI +错误发生”例外!