Подтвердить что ты не робот

Почему Image.FromFile иногда хранит дескриптор файла?

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

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

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

  • NB: Я не делаю ничего глупого, как держать объект Image лежащим - и даже если бы я был woudlnt ожидать, что дескриптор файла будет активным
4b9b3361

Ответ 1

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

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

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

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

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

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

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

Ответ 2

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

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

Ответ 3

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;

Ответ 4

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

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

Используемое выражение является сокращением для

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

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

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

Ответ 5

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

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

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

Ответ 6

Я должен был указать пальцем на сборщика мусора. Оставляя это, на самом деле не проблема, если вы на милость Мусорной коллекции.

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

...

Кажется, это полный хак...

Ответ 7

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

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

Ответ 8

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