Почему Image.FromFile иногда держит дескриптор файла открытым? - PullRequest
20 голосов
/ 25 апреля 2009

Я много работаю с изображениями в GDI + в .NET в приложении ASP.NET.

Я часто нахожу, что Image.FromFile () держит дескриптор файла открытым.

Почему это? Каков наилучший способ открыть изображение без сохранения дескриптора файла.

  • NB. Я не делаю ничего глупого, например, чтобы объект Image лежал без дела - и даже если бы я был так, я бы не ожидал, что дескриптор файла останется активным

Ответы [ 8 ]

35 голосов
/ 09 июля 2009

Я прошел через то же путешествие, что и несколько других постеров в этой теме. Вещи, которые я отметил:

  1. Использование Image.FromFile кажется непредсказуемым, когда он выпускает дескриптор файла. При вызове Image.Dispose () не удалось освободить дескриптор файла во всех случаях.

  2. Использование FileStream и метода Image.FromStream работает и освобождает дескриптор файла, если вы вызываете Dispose () для FileStream или заключаете все это в инструкцию Using {}, как рекомендовано Kris. Однако если вы затем попытаетесь сохранить объект Image в поток, метод Image.Save выдает исключение "Общая ошибка произошла в GDI +" . Предположительно что-то в методе Save хочет знать о исходном файле.

  3. Подход Стивена сработал для меня. Мне удалось удалить исходный файл с объектом изображения в памяти. Я также смог сохранить изображение в виде потока и файла (мне нужно было сделать обе эти вещи). Я также смог сохранить в файл с тем же именем, что и исходный файл, что задокументировано как невозможное, если вы используете метод Image.FromFile (я нахожу это странным, поскольку, конечно, это наиболее вероятный вариант использования, но эй .)

Итак, чтобы подвести итог, откройте свое изображение так:

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

Вы можете свободно управлять им (и исходным файлом) по своему усмотрению.

14 голосов
/ 25 апреля 2009

У меня была такая же проблема, и я обратился к чтению файла, используя

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

5 голосов
/ 25 апреля 2009

Image.FromFile сохраняет дескриптор файла открытым, пока изображение не будет удалено. Из MSDN :

"Файл остается заблокированным, пока изображение не будет удалено."

Используйте Image.FromStream, и у вас не будет проблем.

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

Редактировать: (год и чуть позже)

Приведенный выше код опасен, поскольку он непредсказуем, в какой-то момент времени (после закрытия файлового потока) вы можете получить ужас «В 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;
1 голос
/ 25 апреля 2011

Я также перепробовал все ваши советы (ReadAllBytes, FileStream => FromStream => newBitmap (), чтобы сделать копию и т. Д.), И все они работали. Тем не менее, я хотел бы знать, если бы вы могли найти что-то более короткое, и

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

, похоже, тоже работает, так как располагает дескриптором файла, а также исходным объектом Image и создает новый объект Bitmap, который не зависит от исходного файла и, следовательно, может быть сохранен в потоке или файле без ошибок. .

1 голос
/ 25 апреля 2009

Убедитесь, что вы утилизируете правильно.

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

Выражение использования сокращенно для

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

@ Rex в случае Image.Dispose вызывает GdipDisposeImage extern / native Win32 в своем Dispose ().

IDisposable используется в качестве механизма для освобождения неуправляемых ресурсов (какие дескрипторы файлов)

0 голосов
/ 06 декабря 2015

Я только что столкнулся с той же проблемой, когда пытался объединить несколько одностраничных файлов 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()
0 голосов
/ 13 июля 2009

Как уже упоминалось выше, обходной путь Microsoft вызывает ошибку GDI + после загрузки нескольких изображений. Решение VB для меня, как упомянуто выше Стивеном, является

picTemp.Image = Image.FromStream(New System.IO.MemoryStream(My.Computer.FileSystem.ReadAllBytes(strFl)))
0 голосов
/ 25 апреля 2009

Я бы указал пальцем на сборщик мусора. Оставить все как есть - не проблема, если вы зависите от Сборщика мусора.

У этого парня была похожая жалоба ... и он нашел обходной путь использования объекта 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;
    }

...

Это похоже на полный взлом ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...