2009-04-25 38 views
20

我做的GDI + .NET中大量的圖像處理在ASP.NET應用程序。爲什麼Image.FromFile有時會使文件句柄保持打開狀態?

我經常發現Image.FromFile()是保持文件句柄開放。

這是爲什麼?沒有保留文件句柄的情況下打開圖像的最佳方式是什麼?

  • 注:我沒有做任何愚蠢的事喜歡養躺在附近的Image對象 - 即使我是我woudlnt預計文件句柄保持活躍
+0

您確定FromFile正在那麼做嗎?傻,我知道,但你可以使用句柄(SysInternal utility)來驗證句柄確實來自FromFile。 – 2009-04-25 12:35:43

回答

35

我在這個主題上經歷了和其他海報一樣的旅程。我注意到的事情:

  1. 使用Image.FromFile在釋放文件句柄時似乎不可預知。 調用Image.Dispose()在所有情況下都沒有釋放文件句柄。

  2. 使用一個FileStream和Image.FromStream方法作品,並釋放。如果您上的FileStream調用Dispose()或包裹整個事情在使用{}語句所建議的克里斯文件句柄。但是,如果您嘗試將Image對象保存到流中,則Image.Save方法將引發異常「GDI中發生的一般錯誤」。據推測,Save方法中的某些東西想知道原始文件。

  3. 史蒂文的方法爲我工作。我能夠用內存中的Image對象刪除原始文件。我也能夠將圖像保存到一個流和一個文件(我需要做這兩件事)。我也能夠保存到與原始文件同名的文件中,如果使用Image.FromFile方法記錄爲不可能的東西(我發現這很奇怪,因爲這肯定是最可能的用例,但嘿嘿。)

所以總結一下,打開你的圖片是這樣的:

Image img = Image.FromStream(new MemoryStream(File.ReadAllBytes(path))); 

你就可以自由操縱它(原始文件),你認爲合適。

0

我會點我手指在垃圾收集器。如果你受到垃圾收集的束縛,留下它並不是真正的問題。

這傢伙也有類似的complaint ...,他發現使用FileStream對象,而不是直接從文件加載的解決方法。

public static Image LoadImageFromFile(string fileName) 
{ 
    Image theImage = null; 

    fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read); 
    { 
     byte[] img; 
     img = new byte[fileStream.Length]; 
     fileStream.Read(img, 0, img.Length); 
     fileStream.Close(); 
     theImage = Image.FromStream(new MemoryStream(img)); 
     img = null; 
    } 

...

這似乎是一個完整的劈...

+0

frick是downvote爲什麼? – ojblass 2009-05-05 03:32:26

+0

也許downvote是「看起來像一個完整的黑客」? (不是我)。無論如何,這不是一個黑客。圖像確實保持文件打開,所以如果你想斷開連接,你必須創建自己的副本。打開內存流是一種方法。將圖像渲染到另一個圖像是另一個。無論哪種方式,你都不能依賴圖像從手柄斷開連接 - 它保留在它上面。 :( – Mordachai 2012-03-20 21:04:50

14

我有同樣的問題,使出閱讀使用

return Image.FromStream(new MemoryStream(File.ReadAllBytes(fileName)));

+2

不知道爲什麼我的答案被拒絕了,它會在沒有保持句柄打開的情況下讀取 – Steven 2009-04-25 12:37:44

1

使文件確定你正在處置。

using (Image.FromFile("path")) {} 

的使用表達在Image.Dispose的情況下簡寫

IDisposable obj; 
try { } 
finally 
{ 
    obj.Dispose(); 
} 

@Rex它調用GdipDisposeImage的extern /機Win32呼叫在它的處置()。

IDisposable的是作爲一種機制來釋放非託管資源(哪個文件句柄)

+0

在Image的情況下,Dispose實際上做了什麼?它是否釋放文件系統句柄,非託管內存等? – 2009-04-30 02:38:20

4

Image.FromFile保持,直到圖像設置在文件處理打開。從MSDN

「的文件保持鎖定,直到該圖片被設置」。

使用Image.FromStream,你不會有問題。

using(var fs = new FileStream(filename, FileMode.Open, FileAccess.Read)) 
{ 
    return Image.FromStream(fs); 
} 

編輯:(一年稍後)

上面的代碼是危險的,因爲它是不可預測的,在某個時間點(關閉FILESTREAM之後)你可以得到可怕的「GDI +發生了一般性錯誤」。我會將其修改爲:

Image tmpImage; 
Bitmap returnImage; 

using(var fs = new FileStream(filename, FileMode.Open, FileAccess.Read)) 
{ 
    tmpImage = Image.FromStream(fs); 
    returnImage = new Bitmap(tmpImage); 
    tmpImage.Dispose(); 
} 

return returnImage; 
+0

我認爲FileStream也需要清理。 – 2009-04-30 02:31:30

+0

絕對需要清理。感謝您修復帖子。 – 2009-04-30 15:54:55

0

如上所述,微軟解決了幾個圖像加載後導致GDI +錯誤。如上由史蒂芬提到我的VB的解決方案是

picTemp.Image = Image.FromStream(New System.IO.MemoryStream(My.Computer.FileSystem.ReadAllBytes(strFl))) 
1

我也嘗試了所有的技巧(ReadAllBytes文件流=> FromStream => newBitmap()進行復制等),他們所有的工作。不過,我想知道,如果你能找到的東西短,

using (Image temp = Image.FromFile(path)) 
{ 
    return new Bitmap(temp); 
} 

似乎工作,也因爲其配置文件處理以及原始圖像輸入對象,並創建一個新的位圖對象,即獨立於原始文件,因此可以無誤地保存到流或文件中。

0

我剛剛遇到同樣的問題,我試圖將多個單頁TIFF文件合併爲一個多部分TIFF圖像。我需要使用Image.Save()和「Image.SaveAdd()`:https://msdn.microsoft.com/en-us/library/windows/desktop/ms533839%28v=vs.85%29.aspx

在我的情況的解決辦法是儘快撥打‘.Dispose()’爲每個圖像的,因爲我和他們做:

' Iterate through each single-page source .tiff file 
Dim initialTiff As System.Drawing.Image = Nothing 
For Each filePath As String In srcFilePaths 

    Using fs As System.IO.FileStream = File.Open(filePath, FileMode.Open, FileAccess.Read) 
     If initialTiff Is Nothing Then 
      ' ... Save 1st page of multi-part .TIFF 
      initialTiff = Image.FromStream(fs) 
      encoderParams.Param(0) = New EncoderParameter(Encoder.Compression, EncoderValue.CompressionCCITT4) 
      encoderParams.Param(1) = New EncoderParameter(Encoder.SaveFlag, EncoderValue.MultiFrame) 
      initialTiff.Save(outputFilePath, encoderInfo, encoderParams) 
     Else 
      ' ... Save subsequent pages 
      Dim newTiff As System.Drawing.Image = Image.FromStream(fs) 
      encoderParams = New EncoderParameters(2) 
      encoderParams.Param(0) = New EncoderParameter(Encoder.Compression, EncoderValue.CompressionCCITT4) 
      encoderParams.Param(1) = New EncoderParameter(Encoder.SaveFlag, EncoderValue.FrameDimensionPage) 
      initialTiff.SaveAdd(newTiff, encoderParams) 
      newTiff.Dispose() 
     End If 
    End Using 

Next 

' Make sure to close the file 
initialTiff.Dispose() 
相關問題