2009-04-25 21 views
20

Wykonuję wiele przetwarzania obrazu w GDI + w .NET w aplikacji ASP.NET.Dlaczego Image.FromFile utrzymuje czasami otwarty uchwyt pliku?

Często stwierdzam, że Image.FromFile() utrzymuje otwarty uchwyt pliku.

Dlaczego tak jest? Jaki jest najlepszy sposób na otworzenie obrazu bez zachowania uchwytu pliku.

  • UWAGA: Nie robię nic głupiego jak utrzymanie obiektu obrazu leżącego wokół - a nawet gdybym był woudlnt Spodziewam uchwyt plik należy przechowywać aktywne
+0

Czy jesteś pewien, że FromFile to robi? Głupio, wiem, ale możesz użyć handle (narzędzia SysInternal), aby sprawdzić, czy uchwyt rzeczywiście pochodzi z FromFile. –

Odpowiedz

35

Przeszedłem tę samą podróż, co kilka innych plakatów w tym wątku.Rzeczy, które zauważyłem:

  1. Użycie Image.FromFile wydaje się nieprzewidywalne, gdy zwalnia uchwyt pliku. Wywołanie funkcji Image.Dispose() nie zwolniło uchwytu pliku we wszystkich przypadkach.

  2. Użycie metody FileStream i Image.FromStream działa i zwalnia uchwyt pliku, jeśli wywołasz Dispose() na FileStream lub zawiniesz całość w instrukcji Using {} zalecanej przez Krisa. Jeśli jednak spróbujesz zapisać obiekt Image do strumienia, metoda Image.Save zgłasza wyjątek "Ogólny błąd wystąpił w GDI +". Prawdopodobnie coś w metodzie Save chce wiedzieć o pliku źródłowym.

  3. Steven's approach pracował dla mnie. Udało mi się usunąć plik źródłowy z obiektem Image w pamięci. Byłem także w stanie zapisać obraz zarówno w strumieniu, jak i pliku (potrzebowałem zrobić obie te rzeczy). Byłem również w stanie zapisać do pliku o tej samej nazwie co plik źródłowy, co jest udokumentowane jako niemożliwe, jeśli używasz metody Image.FromFile (uważam to za dziwne, ponieważ z pewnością jest to najbardziej prawdopodobny przypadek użycia, ale hej .)

więc podsumować, otwórz swój obraz tak:

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

Jesteś wtedy swobodnie manipulować (i plik pochodzenia) zgodnie z potrzebami.

+0

sprytny :-) dzięki –

+0

Webapp, który mam w produkcji, który używa tej techniki wydaje się przeciekać pamięć. Mam go uruchomionego na 6 serwerach i po kilku dniach odpowiedni proces ASP.NET używa> 2 GB pamięci. Zrobiłem poprawkę, ustawiając odpowiednią pulę aplikacji, aby odtworzyć ją po tym, jak osiągnie 500 MB zużycia pamięci lub codziennie o 2 rano. Nie mam teraz czasu na zbadanie tej sprawy, ale kiedy to zrobię, opublikuję tutaj moje rozwiązanie. – Jamie

+0

Czy znalazłeś/naprawiłeś swój problem? – Nifle

0

musiałbym wskazać mój palec w Garbage Collectorze. Opuszczenie go nie jest problemem, jeśli jesteś na łasce Garbage Collection.

Ten facet miał podobne complaint ... i znalazł obejście użycia obiektu FileStream zamiast ładowania bezpośrednio z pliku.

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; 
    } 

...

Wydaje się jak kompletny siekać ...

+0

o co jest farsa? – ojblass

+0

możliwe, że głosowanie brzmiało "wygląda na kompletny hack"? (nie ja). W każdym razie to nie jest hack. Obraz nie otwiera pliku, więc jeśli chcesz się rozłączyć, musisz utworzyć własną kopię. Otwieranie strumienia pamięci jest jednym ze sposobów. Renderowanie obrazu na inny obraz jest kolejnym. Tak czy inaczej, nie można polegać na odłączaniu obrazu od uchwytu - to się trzyma. :( – Mordachai

14

miałem ten sam problem i uciekają się do odczytu pliku, używając

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

+2

Nie wiem, dlaczego moja odpowiedź została odrzucona, będzie czytana bez trzymania uchwytu otwartego – Steven

1

Producent masz pewność, że prawidłowo się Pozbywasz.

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

Using ekspresja jest skrótem

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

@Rex w przypadku Image.Dispose wywołuje GdipDisposeImage extern/natywną wywołanie Win32 w to Dispose().

IDisposable służy jako mechanizm wolnych zasobów niezarządzalny (który obsługuje plików są)

+0

W przypadku obrazu, co właściwie robi Dispose? Czy zwalnia obsługę plików systemu, niezarządzaną pamięć itp.? –

4

Image.FromFile utrzymuje otwarty plik obsłużyć aż obraz zostanie umieszczony. Od ": ", "Plik pozostaje zablokowany, dopóki obraz nie zostanie usunięty."

Użyj Image.FromStream, a nie będziesz miał problemu.

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

Edit: (rok i nieco później)

Powyższy kod jest niebezpieczny, ponieważ jest nieprzewidywalny, w pewnym momencie (po zamknięciu filestream) Państwo może uzyskać dreaded "Wystąpił ogólny błąd w GDI +". Zmienię go na:

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

Myślę, że FileStream również musi zostać oczyszczony. –

+0

Absolutnie musi być oczyszczony. Dzięki za naprawienie postu. –

0

Jak wspomniano powyżej, działania firmy Microsoft powodują błąd GDI + po załadowaniu kilku obrazów. Rozwiązanie VB dla mnie jak wspomniano powyżej Steven

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

Próbowałem również wszystkie wskazówki (ReadAllBytes, FileStream => FromStream => newBitmap(), aby wykonać kopię, itd.) I wszyscy pracowali. Jednak, zastanawiałem się, czy można znaleźć coś krótsze, a

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

wydaje się działać także jako rozstrzygającego plik obsłużyć jak również oryginalny obraz-obiekt i tworzy nowe bitmapy obiektu, który jest niezależnie od oryginalnego pliku i dlatego można go zapisać do strumienia lub pliku bez błędów.

+0

Chociaż jest to prawdopodobnie mniej wydajne, jest to jedyne rozwiązanie, które wydaje się działać dla mnie. –

0

Po prostu napotkałem ten sam problem, w którym próbowałem scalić wiele pojedynczych plików TIFF w jeden wieloczęściowy obraz TIFF. Musiałem użyć Image.Save() i „Image.SaveAdd()`: https://msdn.microsoft.com/en-us/library/windows/desktop/ms533839%28v=vs.85%29.aspx

Rozwiązaniem w moim przypadku było nazwać «.Dispose()» dla każdego z obrazów, jak tylko było z nimi zrobić:

' 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() 
Powiązane problemy