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

Бесплатный файл заблокирован новым Bitmap (filePath)

У меня есть изображение PictureBox, указывающее на определенный файл "A", во время выполнения я хочу изменить изображение PictureBox на другое "B", но я получаю следующую ошибку:

"Первое исключение исключения типа" System.IO.IOException "произошло в mscorlib.dll Дополнительная информация: Процесс не может получить доступ к файлу "A", потому что он используется другим процессом ".

Я устанавливаю изображение следующим образом:

pbAvatar.Image = new Bitmap(filePath);

Как я могу разблокировать первый файл?

Спасибо заранее!

4b9b3361

Ответ 1

Использование filestream разблокирует файл после его чтения и удаления:

using (var fs = new System.IO.FileStream("c:\\path to file.bmp", System.IO.FileMode.Open))
{
    var bmp = new Bitmap(fs);
    pct.Image = (Bitmap) bmp.Clone();
}

Изменить: Обновлено, чтобы разрешить размещение исходного растрового изображения и разрешить закрытие FileStream.

ЭТО ОТВЕТ НЕ БЕЗОПАСНО - см. комментарии и см. обсуждение в ответ net_prog. Редактировать для использования Clone не делает его более безопасным - Clone клонирует все поля, включая ссылку на поток, что в определенных обстоятельствах может вызвать проблему.

Ответ 2

Вот мой подход к открытию изображения без блокировки файла...

public static Image FromFile(string path)
{
    var bytes = File.ReadAllBytes(path);
    var ms = new MemoryStream(bytes);
    var img = Image.FromStream(ms);
    return img;
}

ОБНОВЛЕНИЕ: Я провел несколько перфекционных тестов, чтобы узнать, какой метод был самым быстрым. Я сравнил его с ответом @net_progs "копировать из растрового изображения" (который, кажется, ближе всего подходит, хотя и имеет некоторые проблемы). Я загрузил изображение 10000 раз для каждого метода и вычислил среднее время на изображение. Вот результаты:

Loading from bytes: ~0.26 ms per image.
Copying from bitmap: ~0.50 ms per image.

Результаты, похоже, имеют смысл, поскольку вам нужно дважды создать изображение, используя копию из метода растрового изображения.

Ответ 3

Это общий вопрос о блокировке, широко обсуждаемый в Интернете.

Предлагаемый трюк с потоком не работает, на самом деле он работает изначально, но вызывает проблемы позже. Например, он загрузит изображение, и файл останется разблокированным, но если вы попытаетесь сохранить загруженное изображение с помощью метода Save(), оно будет генерировать общее исключение GDI +.

Далее, путь с репликацией на пиксель не кажется сплошным, по крайней мере, это шумно.

То, что я нашел, описано здесь: http://www.eggheadcafe.com/microsoft/Csharp/35017279/imagefromfile--locks-file.aspx

Так должно загружаться изображение:

Image img;
using (var bmpTemp = new Bitmap("image_file_path"))
{
    img = new Bitmap(bmpTemp);
}

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

Ответ 4

Вы не можете удалять/закрывать поток, пока растровый объект все еще использует его. (Возможно ли, что объект растрового изображения будет иметь к нему доступ снова, будет детерминированным только в том случае, если вы знаете, с каким типом файла вы работаете, и какие именно операции вы будете выполнять. Например, для изображений формата .gif, поток закрыт раньше конструктор возвращается.)

Клон создает "точную копию" растрового изображения (по документации, ILSpy показывает, что он вызывает собственные методы, поэтому он слишком много для отслеживания прямо сейчас), он также копирует данные Stream, а также не будет, t - точная копия.

Лучше всего создать идеальную копию изображения - хотя YMMV (с определенными типами изображений может быть более одного кадра, или вам, возможно, придется копировать данные палитры). Но для большинства изображений, это работает:

static Bitmap LoadImage(Stream stream)
{
    Bitmap retval = null;

    using (Bitmap b = new Bitmap(stream))
    {
        retval = new Bitmap(b.Width, b.Height, b.PixelFormat);
        using (Graphics g = Graphics.FromImage(retval))
        {
            g.DrawImage(b, Point.Empty);
            g.Flush();
        }
    }

    return retval;
}

И затем вы можете вызвать его так:

using (Stream s = ...)
{
    Bitmap x = LoadImage(s);
}

Ответ 5

Здесь техника, которую я сейчас использую, и, кажется, работает лучше всего. Преимущество состоит в том, что в качестве исходного файла создается объект Bitmap с тем же самым пиксельным (24-битным или 32-битным) разрешением и разрешением (72 dpi, 96 dpi и т.д.).

  // ImageConverter object used to convert JPEG byte arrays into Image objects. This is static 
  //  and only gets instantiated once.
  private static readonly ImageConverter _imageConverter = new ImageConverter();

Это можно использовать так часто, как нужно, следующим образом:

     Bitmap newBitmap = (Bitmap)_imageConverter.ConvertFrom(File.ReadAllBytes(fileName));

Изменить: Здесь обновление описанной выше техники: fooobar.com/questions/85936/...

Ответ 6

(Принятый ответ неверен. Когда вы пытаетесь выполнить LockBits(...) на клонированном растровом изображении, вы столкнетесь с ошибками GDI +.)


Я вижу только 3 способа избавиться от этого:
  • скопируйте файл во временный файл и откройте, что простой способ new Bitmap(temp_filename)
  • откройте файл, прочитайте изображение, создайте копию размера пикселя-пикселя (не Clone()) и удалите первый растровый рисунок
  • (принять функцию заблокированного файла)

Ответ 7

Прочитайте его в потоке, создайте растровое изображение, закройте поток.

Ответ 8

Три года назад я написал программу просмотра изображений, чтобы посмотреть, смогу ли я это сделать. На прошлой неделе я добавил код для сканирования изображений. (Я планирую добавить это в программу генеалогии после того, как я получу ошибки.) Чтобы обрезать неиспользуемую область, у меня есть вызов программы MSPaint с именем файла. Я редактирую его там, затем сохраняю. Когда я закрываю Paint, изображение показывает изменения.
Я получал ошибку в Paint о заблокированном файле, если я сделал что-то с изображением. Я меняю программу на блокировку изображения, используя Image, FromStream(). Я больше не получаю это сообщение в Paint. (Моя программа находится в VB 2010.)

Ответ 9

Насколько я знаю, это на 100% безопасно, так как полученный образ на 100% создается в памяти без каких-либо связанных ресурсов и без открытых потоков, оставшихся в памяти. Он действует как любой другой Bitmap, созданный из конструктора, который не указывает какие-либо источники ввода, и в отличие от некоторых других ответов здесь он сохраняет исходный формат пикселей, то есть он может использоваться в индексированных форматах.

На основе этого ответа, но с дополнительными исправлениями и без импорта внешней библиотеки.

/// <summary>
/// Clones an image object to free it from any backing resources.
/// Code taken from /info/340802/filedelete-failing-when-imagefromfile-was-called-prior-it-despite-making-copy-of-loaded-image-and-destroying-original-one/1616671#1616671 with some extra fixes.
/// </summary>
/// <param name="sourceImage">The image to clone</param>
/// <returns>The cloned image</returns>
public static Bitmap CloneImage(Bitmap sourceImage)
{
    Rectangle rect = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height);
    Bitmap targetImage = new Bitmap(rect.Width, rect.Height, sourceImage.PixelFormat);
    targetImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
    BitmapData sourceData = sourceImage.LockBits(rect, ImageLockMode.ReadOnly, sourceImage.PixelFormat);
    BitmapData targetData = targetImage.LockBits(rect, ImageLockMode.WriteOnly, targetImage.PixelFormat);
    Int32 actualDataWidth = ((Image.GetPixelFormatSize(sourceImage.PixelFormat) * rect.Width) + 7) / 8;
    Int32 h = sourceImage.Height;
    Int32 origStride = sourceData.Stride;
    Int32 targetStride = targetData.Stride;
    Byte[] imageData = new Byte[actualDataWidth];
    IntPtr sourcePos = sourceData.Scan0;
    IntPtr destPos = targetData.Scan0;
    // Copy line by line, skipping by stride but copying actual data width
    for (Int32 y = 0; y < h; y++)
    {
        Marshal.Copy(sourcePos, imageData, 0, actualDataWidth);
        Marshal.Copy(imageData, 0, destPos, actualDataWidth);
        sourcePos = new IntPtr(sourcePos.ToInt64() + origStride);
        destPos = new IntPtr(destPos.ToInt64() + targetStride);
    }
    targetImage.UnlockBits(targetData);
    sourceImage.UnlockBits(sourceData);
    // For indexed images, restore the palette. This is not linking to a referenced
    // object in the original image; the getter of Palette creates a new object when called.
    if ((sourceImage.PixelFormat & PixelFormat.Indexed) != 0)
        targetImage.Palette = sourceImage.Palette;
    // Restore DPI settings
    targetImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
    return targetImage;
}

Чтобы позвонить, просто используйте:

/// <summary>Loads an image without locking the underlying file.</summary>
/// <param name="path">Path of the image to load</param>
/// <returns>The image</returns>
public static Bitmap LoadImageSafe(String path)
{
    using (Bitmap sourceImage = new Bitmap(path))
    {
        return CloneImage(sourceImage);
    }
}