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

时间:2011-01-26 11:17:03

标签: c# image file picturebox

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

  

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

我按如下方式设置图像:

pbAvatar.Image = new Bitmap(filePath);

如何解锁第一个文件?

10 个答案:

答案 0 :(得分:49)

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

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.

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

更新: 如果你需要BitMap,你可以这样做:

return (Bitmap)Image.FromStream(ms);

答案 1 :(得分:38)

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

使用流建议的技巧不起作用,实际上它最初有效,但稍后会导致问题。例如,它将加载图像并且文件将保持解锁状态,但如果您尝试通过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);
}

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

答案 2 :(得分:25)

使用文件流会在文件被读取并处理后解锁文件:

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's answer中的讨论。编辑以使用Clone并不会使其更安全 - 克隆克隆所有字段,包括文件流引用,在某些情况下会导致问题。

答案 3 :(得分:5)

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

Clone创建位图的“精确副本”(每个文档; 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;
}

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

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

答案 4 :(得分:4)

这是我目前使用的技术,似乎效果最好。它的优点是可以生成具有相同像素格式(24位或32位)和分辨率(72 dpi,96 dpi等)的Bitmap对象作为源文件。

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

编辑: 以下是上述技术的更新:https://stackoverflow.com/a/16576471/253938

答案 5 :(得分:3)

The accepted answer错误。当你在克隆位图上尝试LockBits(...)时,最终会遇到GDI +错误。)

<小时/> 我只看到了3种方法:

  • 将您的文件复制到临时文件,并以简单方式new Bitmap(temp_filename)
  • 打开
  • 打开文件,读取图片,创建像素大小像素格式的副本(不要Clone())并处理第一个位图
  • (接受锁定文件功能)

答案 6 :(得分:1)

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

基于this answer,但有额外的修复,没有外部库导入。

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

要打电话,只需使用:

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

或者,从字节:

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

答案 7 :(得分:0)

将其读入流中,创建位图,关闭流。

答案 8 :(得分:0)

我建议使用 PixelMap (在NuGet上可用) 或Github

非常好用,比.NET中的标准位图要快

  download(user) {


    this.clientAPI.get(Endpoints.DOWNLOAD).toPromise()
      .then(res => {
      
        let blob = new Blob([new Uint8Array(res.file)], { type: 'application/zip' });
          console.log('blob',blob);
        let a = document.createElement('a');
        a.href = (URL.createObjectURL(blob));
        a.download = res.filename;
        document.body.appendChild(a);
        a.click();
         a.remove();
        //  this.downloadComplete(user);
      })
      .catch(err => console.error("download error = ", err))
  }

Here is my backend code (Node Js):-

exports.download = function (req, res) {

  let file = process.env.NOCE_ENV === 'local' ? `${process.cwd()}/server/downloads/eFAST-Release-20.5.17.19.zip` :
    `${process.cwd()}/server/downloads/eFAST-Release-20.5.17.19.zip`;
  let filename = path.basename(file);
  res.setHeader('Content-disposition', 'attachment; filename=' + filename);
  res.setHeader('Content-type', "application/zip");
  let filestream = fs.createReadStream(file);
  
 // console.log('filestream',filenter code hereestream)
  res.jsonp({ filename: filename, file: filestream });
};

答案 9 :(得分:-2)

三年前,我写了一个图片浏览器程序,看看能否做到。上周,我添加了扫描图像的代码。 (我计划在收到错误后将其添加到家谱程序中。) 要裁剪未使用的区域,我正在使用文件名调用MSPaint程序。我在那里编辑然后保存。当我关闭Paint时,图像会显示更改 如果我对图像做了任何事情,我在Paint中收到关于文件被锁定的错误。我使用Image,FromStream()更改程序以锁定图像。我不再在Paint中获得该消息。 (我的程序是在VB 2010中。)