2010-08-02 31 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 +錯誤」異常!

相關問題