Fichier Libre verrouillé par la nouvelle Bitmap (filePath)

j'ai l'Image d'un PictureBox pointant vers un fichier "A", dans l'exécution de fois que je veux changer l'Image de la PictureBox à un autre "B", mais j'obtiens l'erreur suivante:

"Une exception de première chance de type 'System.IO.IOException " s'est produite dans mscorlib.DLL Information supplémentaire: le processus ne peut pas accéder au fichier "A" parce qu'il est utilisé par un autre processus."

je mets l'Image comme suit:

pbAvatar.Image = new Bitmap(filePath);

Comment débloquer le premier fichier?

Merci d'avance!

32
demandé sur MrCatacroquer 2011-01-26 14:17:03

9 réponses

L'utilisation d'une filestream déverrouillera le fichier une fois qu'il aura été lu et éliminé:

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

Edit: mis à Jour pour permettre la bitmap d'origine pour être éliminé, et permettre à l'FileStream pour être fermé.

CETTE RÉPONSE n'EST PAS SÛR - Voir les commentaires, et de voir la discussion dans net_prog la réponse de . L'édition à utiliser Clone ne rend pas plus sûr - Clone clones tous champs, y compris la référence filestream, qui, dans certaines circonstances, posera un problème.

27
répondu Pondidum 2017-10-15 01:28:07

Voici mon approche pour ouvrir une image sans verrouiller le fichier...

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

mise à jour: j'ai fait quelques tests perf pour voir quelle méthode était la plus rapide. Je l'ai comparé à la réponse de @net_progs "copier à partir de bitmap" (qui semble être la plus proche à corriger, mais qui a quelques problèmes). J'ai chargé l'image 10000 fois pour chaque méthode et calculé le temps moyen par image. Voici les résultats:

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

Les résultats semblent faire logique, puisque vous devez créer l'image deux fois à l'aide de la copie de bitmap.

mise à jour: si vous avez besoin D'un BitMap vous pouvez faire:

return (Bitmap)Image.FromStream(ms);
44
répondu Brian 2018-09-07 09:10:28

C'est une question de verrouillage courante largement discutée sur le web.

le truc suggéré avec stream ne fonctionnera pas , en fait il fonctionne d'abord, mais provoque des problèmes plus tard. Par exemple, il chargera l'image et le fichier restera déverrouillé, mais si vous essayez de sauvegarder l'image chargée via la méthode Save (), il lancera une exception GDI+ Générique.

ensuite, la manière avec la réplication par pixel ne semble pas être solide, au moins il est bruyant.

ce que j'ai trouvé de travail est décrit ici: http://www.eggheadcafe.com/microsoft/Csharp/35017279/imagefromfile--locks-file.aspx

voici comment l'image doit être chargée:

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

je cherchais une solution à ce problème et cette méthode fonctionne très bien pour moi jusqu'à présent, donc j'ai décidé de la décrire, puisque j'ai constaté que beaucoup de gens conseillent l'approche de flux incorrecte ici et sur le web.

37
répondu net_prog 2012-01-02 14:01:30

vous ne pouvez pas disposer / fermer un flux alors qu'un objet bitmap l'utilise encore. (La question de savoir si l'objet bitmap aura à nouveau besoin d'y accéder n'est déterministe que si vous savez avec quel type de fichier vous travaillez et exactement quelles opérations vous effectuerez. -- par exemple pour certains .GIF format images, le flux est fermé avant le retour du constructeur.)

Clone crée une "copie exacte" du bitmap (par documentation; ILSpy le montre appelant natif méthodes, donc c'est trop à dépister en ce moment) probablement, il copie ce flux de données aussi -- ou alors ce ne serait pas une copie exacte.

votre meilleur pari est de créer une réplique parfaite en pixel de l'image -- bien que YMMV (avec certains types d'images il peut y avoir plus d'une image, ou vous pouvez avoir à copier des données de palette aussi bien.) Mais pour la plupart des images, cela fonctionne:

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

Et alors vous pouvez l'invoquer comme tel:

using (Stream s = ...)
{
    Bitmap x = LoadImage(s);
}
6
répondu BrainSlugs83 2011-11-01 21:15:59

Voici la technique que j'utilise actuellement, et qui semble fonctionner le mieux. Il a l'avantage de produire un objet Bitmap avec le même format de pixel (24 bits ou 32 bits) et la même résolution (72 dpi, 96 dpi, peu importe) que le fichier source.

  // 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();

Ceci peut être utilisé aussi souvent que nécessaire, comme suit:

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

Edit: Voici une mise à jour de la technique ci-dessus: https://stackoverflow.com/a/16576471/253938

4
répondu RenniePet 2017-05-23 11:55:02

( la réponse acceptée est erronée. Lorsque vous essayez de LockBits(...) sur le bitmap cloné, vous rencontrerez éventuellement des erreurs GDI+.)


Je ne vois que 3 façons de sortir de cette:
  • copier votre fichier dans un fichier temporaire et ouvrir que la manière facile new Bitmap(temp_filename)
  • ouvrir votre fichier, Lire l'image, créer une copie de format pixel-size-pixel (ne pas Clone() ) et disposer le premier bitmap
  • (accepter le verrouillage de fichier)
3
répondu Bitterblue 2017-05-23 10:31:36

pour autant que je sache, c'est 100% sûr, puisque l'image résultante est 100% créée en mémoire, sans aucune ressource liée, et sans aucun flux ouvert laissé en mémoire. Il agit comme n'importe quel autre Bitmap qui est créé à partir d'un constructeur qui ne spécifie aucune source d'entrée, et contrairement à certaines des autres réponses ici, il préserve le format de pixel original, ce qui signifie qu'il peut être utilisé sur les formats indexés.

basé sur cette réponse , mais avec des corrections supplémentaires et sans importation de bibliothèque externe.

/// <summary>
/// Clones an image object to free it from any backing resources.
/// Code taken from http://stackoverflow.com/a/3661892/ 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;
}

pour appeler, il suffit d'utiliser:

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

Ou, à partir de bytes:

/// <summary>Loads an image from bytes without leaving open a MemoryStream.</summary>
/// <param name="fileData">Byte array containing the image to load.</param>
/// <returns>The image</returns>
public static Bitmap LoadImageSafe(Byte[] fileData)
{
    using (MemoryStream stream = new MemoryStream(fileData))
    using (Bitmap sourceImage = new Bitmap(stream))    {
    {
        return CloneImage(sourceImage);
    }
}
1
répondu Nyerguds 2018-03-22 20:43:34

Lire dans le flux, créer bitmap, fermer le flux de données.

0
répondu Sasha Reminnyi 2011-01-26 11:21:13

il y a trois ans, j'ai écrit une émission pour voir si je pouvais le faire. La semaine dernière, j'ai ajouté du code pour numériser des images. (J'ai l'intention d'ajouter ceci à un programme de généalogie après avoir sorti les bogues.) Pour recolter la surface inutilisée, J'ai le programme appelé MSPaint avec le nom du fichier. Je l'éditer puis de les enregistrer. Quand je ferme la Peinture, l'image montre les changements.

J'ai eu une erreur de peinture à propos du verrouillage du fichier si j'ai fait quoi que ce soit à l'image. - Je changer le programme pour verrouiller une image,en utilisant Image, FromStream (). Je ne reçois plus ce message dans Paint. (Mon programme est en VB 2010.)

0
répondu Jon Price 2013-12-20 18:27:42