新Bitmap(filePath)锁定的免费文件

MrC*_*uer 38 c# image file picturebox

我有一个PictureBox的图像指向某个文件"A".在执行时我想将PictureBox的图像更改为另一个"B"但我收到以下错误:

"mscorlib.dll中出现'System.IO.IOException'类型的第一次机会异常附加信息:进程无法访问文件"A",因为它正由另一个进程使用."

我将Image设置如下:

pbAvatar.Image = new Bitmap(filePath);
Run Code Online (Sandbox Code Playgroud)

如何解锁第一个文件?

Bri*_*ian 50

这是我打开图像而不锁定文件的方法...

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

更新:我做了一些性能测试,看看哪种方法最快.我将它与@net_progs"从位图复制"答案(这似乎是最接近正确的,但确实有一些问题)进行了比较.我为每种方法加载了10000次图像并计算了每张图像的平均时间.结果如下:

Loading from bytes: ~0.26 ms per image.
Copying from bitmap: ~0.50 ms per image.
Run Code Online (Sandbox Code Playgroud)

结果似乎有意义,因为您必须使用位图方法的副本创建图像两次.

更新:如果您需要BitMap,您可以:

return (Bitmap)Image.FromStream(ms);
Run Code Online (Sandbox Code Playgroud)

  • 您仍然必须在图像的生命周期中保持流打开,这意味着数据在内存中两次.这对于较大的图像是不切实际的. (3认同)
  • 图像在创建后使用了流(抱歉,我不记得它所处的场景).如果你处理它,它将抛出异常.在MemoryStream上调用Dispose并不重要,因为它不使用任何系统资源(例如使用FileStream时的文件锁定).GC会在适当的时候正确清理它. (3认同)
  • @asgerhallas 你肯定想在图像上调用 Dispose。它包含必须销毁的本机 GDI 对象。 (2认同)

net*_*rog 39

这是在网络上广泛讨论的常见锁定问题.

使用流的建议技巧将无法工作,实际上它最初工作,但稍后会导致问题.例如,它将加载图像,文件将保持解锁状态,但如果您尝试通过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);
}
Run Code Online (Sandbox Code Playgroud)

我一直在寻找这个问题的解决方案,这个方法到目前为止对我来说很好,所以我决定对它进行描述,因为我发现许多人在这里和网上都提出了错误的流方法.

  • 这有一个小问题,因为像素格式将始终是32位ARGB,分辨率将始终为96 dpi.对于大多数应用程序来说这可能没问题,但对于某些应用程序,尝试维护源文件的像素格式和分辨率非常重要. (10认同)
  • @ToolmakerSteve 这不适用于索引格式。唯一的方法是使用`LockBits`来复制支持数组,然后恢复调色板。 (2认同)

Pon*_*dum 26

使用文件流将在文件读取和处理后解锁:

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();
}
Run Code Online (Sandbox Code Playgroud)

编辑:已更新以允许处置原始位图,并允许关闭FileStream.

这个答案并不安全 - 请参阅评论,并参见net_prog的答案中的讨论.要使用的编辑Clone不会使它更安全 - 克隆克隆所有字段,包括文件流引用,在某些情况下会导致问题.

  • 没有!坏!"你必须在Bitmap的生命周期内保持流打开." - [MSDN](http://msdn.microsoft.com/en-us/library/z7ha67kw.aspx).由@Computer Linguist发布如下. (4认同)
  • 这个答案仍然失败. (3认同)
  • 这不起作用,它改变了PixelFormat和其他东西 (3认同)
  • 可疑.如[BrainSlugs答案](http://stackoverflow.com/a/7972963/199364)中所述,并经[Anlo](http://stackoverflow.com/a/13935966/199364)验证,"克隆"不会帮助这种情况.其他答案更安全. (3认同)

Bra*_*s83 5

当位图对象仍在使用它时,您无法处置/关闭流.(如果你知道你正在使用什么类型的文件以及你将要执行什么操作,那么位图对象是否需要再次访问它只是确定性的 - 例如对于SOME .gif格式图像,流之前关闭构造函数返回.)

克隆创建位图的"精确副本"(每个文档; ILSpy显示它调用本机方法,所以现在跟踪太多了)可能,它也复制了Stream数据 - 否则它不会是精确的副本.

你最好的选择是创建一个像素完美的图像复制品 - 虽然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;
}
Run Code Online (Sandbox Code Playgroud)

然后你可以像这样调用它:

using (Stream s = ...)
{
    Bitmap x = LoadImage(s);
}
Run Code Online (Sandbox Code Playgroud)

  • 为了保留dpi,在`retval = ...;`add:`retval.SetResolution(b.Horizo​​ntalResolution,b.VerticalResolution);`之后 (2认同)

Nye*_*uds 5

据我所知,这是 100% 安全的,因为生成的图像 100% 是在内存中创建的,没有任何链接资源,并且没有在内存中留下任何打开的流。它的作用类似于Bitmap从不指定任何输入源的构造函数创建的任何其他答案,并且与此处的其他一些答案不同,它保留了原始像素格式,这意味着它可以用于索引格式。

基于这个答案,但有额外的修复并且没有外部库导入。

/// <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;
    Boolean isFlipped = origStride < 0;
    origStride = Math.Abs(origStride); // Fix for negative stride in BMP format.
    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);
    // Fix for negative stride on BMP format.
    if (isFlipped)
        targetImage.RotateFlip(RotateFlipType.Rotate180FlipX);
    // 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;
}
Run Code Online (Sandbox Code Playgroud)

要调用,只需使用:

/// <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);
    }
}
Run Code Online (Sandbox Code Playgroud)

或者,从字节:

/// <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);
    }
}
Run Code Online (Sandbox Code Playgroud)