代码之家  ›  专栏  ›  技术社区  ›  PiotrK

尽管复制加载的图像并销毁原始图像,但在调用image.fromfile之前删除失败。

  •  9
  • PiotrK  · 技术社区  · 15 年前

    更新的

    我使用下面的解决方案(从流中加载图像),但得到了新的问题。img对象是绝对正确的图像类实例,所有字段都填充了正确的值。但呼唤

    img.Save("path/to/new/image.bmp");
    

    它会导致gdi+出现新的异常(system.runtime.interopservices.externalException,在gdi+接口中)——我得到了错误消息,但它是波兰语,我不确定如何翻译它。

    原始问题

    我对C.NET Framework 2.0有问题

    基本上,我正在努力实现:

    Image img = Image.FromFile("Path/To/Image.bmp");
    File.Delete("Path/To/Image.bmp"); // Exception, the file is in use!
    

    删除原始文件时,将图像的副本保存在内存中是很重要的。我觉得奇怪的是.NET仍然在硬盘上锁定文件,尽管它不再需要任何操作(整个图像现在在内存中,不是吗?)

    所以我尝试了这个解决方案:

    Image img = new Image(Image.FromFile("Path/To/Image.bmp")); // Make a copy
                        // this should immiedietaly destroy original loaded image
    File.Delete("Path/To/Image.bmp"); // Still exception: the file is in use!
    

    我能做到:

    Image img = null;
    using(Image imgTmp = Image.FromFile("Path/To/Image.bmp"))
    {
        img = new Bitmap(imgTmp.Width, imgTmp.Height, imgTmp.PixelFormat);
        Graphics gdi = Graphics.FromIage(img);
        gdi.DrawImageUnscaled(imgTmp, 0, 0);
        gdi.Dispose();
        imgTmp.Dispose(); // just to make sure
    }
    File.Delete("Path/To/Image.bmp"); // Works fine
    // So I have img!
    

    但在我看来,这就像用核武器杀死虫子…并引发了另一个问题:GDI不太支持将基于调色板的图像相互绘制(调色板图像在我的收藏中占大多数)。

    我做错什么了吗?有没有更好的方法可以从硬盘上删除内存中的图像和原始文件?

    5 回复  |  直到 8 年前
        1
  •  17
  •   Jordão    11 年前

    这应该可以做到:

      Image img = null;
      using (var stream = File.OpenRead(path)) {
        img = Image.FromStream(stream);
      }
      File.Delete(path);
    

    更新 :不要使用上面的代码!

    我找到了相关的知识库文章: http://support.microsoft.com/?id=814675

    解决方案是如本文所述真正复制位图。我已经对文章提到的两种方式进行了编码(第一种是你正在做的,第二种是你答案中的,但没有使用 unsafe ):

    public static Image CreateNonIndexedImage(string path) { 
      using (var sourceImage = Image.FromFile(path)) { 
        var targetImage = new Bitmap(sourceImage.Width, sourceImage.Height, 
          PixelFormat.Format32bppArgb); 
        using (var canvas = Graphics.FromImage(targetImage)) { 
          canvas.DrawImageUnscaled(sourceImage, 0, 0); 
        } 
        return targetImage; 
      } 
    } 
    
    [DllImport("Kernel32.dll", EntryPoint = "CopyMemory")] 
    private extern static void CopyMemory(IntPtr dest, IntPtr src, uint length); 
    
    public static Image CreateIndexedImage(string path) { 
      using (var sourceImage = (Bitmap)Image.FromFile(path)) { 
        var targetImage = new Bitmap(sourceImage.Width, sourceImage.Height, 
          sourceImage.PixelFormat); 
        var sourceData = sourceImage.LockBits(
          new Rectangle(0, 0, sourceImage.Width, sourceImage.Height), 
          ImageLockMode.ReadOnly, sourceImage.PixelFormat); 
        var targetData = targetImage.LockBits(
          new Rectangle(0, 0, sourceImage.Width, sourceImage.Height), 
          ImageLockMode.WriteOnly, targetImage.PixelFormat); 
        CopyMemory(targetData.Scan0, sourceData.Scan0, 
          (uint)sourceData.Stride * (uint)sourceData.Height); 
        sourceImage.UnlockBits(sourceData); 
        targetImage.UnlockBits(targetData); 
        targetImage.Palette = sourceImage.Palette;
        return targetImage; 
      } 
    } 
    
        2
  •  10
  •   KeithS    11 年前

    您的问题是,新映像仍然知道它来自哪里,已经从旧映像的复制构造函数中获得了文件句柄,因此运行时仍然知道它有一个打开的文件句柄。

    您可能可以使用流来解决此行为:

    Image image;
    FileStream myStream = new FileStream(path);
    
    try
    {
        image = Image.FromStream(myStream);
    }
    finally
    {    
        myStream.Close();
        myStream.Dispose();
    }
    
    //test that you have a valid Image and then go to work.
    

    这里有一个更干净的版本 using 条款:

    Image image;
    using(FileStream myStream = new FileStream(path))
    {
        image = Image.FromStream(myStream);
    }
    //a using clause calls Dispose() at the end of the block,
    //which will call Close() as well
    

    请注意:我没有测试过这个,不能保证它能解决问题,但这似乎是合理的。直接使用流可以控制文件句柄,而不是图像实现,因此可以确保您的程序在需要时释放资源。

        3
  •  1
  •   Mohammed Atif Sami    13 年前

    只放

    GC.Collect();

    最后它应该可以正常工作

        4
  •  0
  •   PiotrK    15 年前

    这样做很好,缺点是它需要“不安全”的编译。

    从加载完成时终止的流加载图像时的版本导致无法通过经典GDI将图像保存到光盘+

    public static unsafe Image LoadImageSafe(string path)
    {
        Image ret = null;
        using (Image imgTmp = Image.FromFile(path))
        {
            ret = new Bitmap(imgTmp.Width, imgTmp.Height, imgTmp.PixelFormat);
            if (imgTmp.PixelFormat == PixelFormat.Format8bppIndexed)
            {
                ColorPalette pal = ret.Palette;
                for (int i = 0; i < imgTmp.Palette.Entries.Length; i++)
                    pal.Entries[i] = Color.FromArgb(imgTmp.Palette.Entries[i].A,
                        imgTmp.Palette.Entries[i].R, imgTmp.Palette.Entries[i].G,
                        imgTmp.Palette.Entries[i].B);
                ret.Palette = pal;
                BitmapData bmd = ((Bitmap)ret).LockBits(new Rectangle(0, 0,
                    imgTmp.Width, imgTmp.Height), ImageLockMode.WriteOnly,
                    PixelFormat.Format8bppIndexed);
                BitmapData bmd2 = ((Bitmap)imgTmp).LockBits(new Rectangle(0, 0,
                    imgTmp.Width, imgTmp.Height), ImageLockMode.ReadOnly,
                    PixelFormat.Format8bppIndexed);
    
                Byte* pPixel = (Byte*)bmd.Scan0;
                Byte* pPixel2 = (Byte*)bmd2.Scan0;
    
                for (int Y = 0; Y < imgTmp.Height; Y++)
                {
                    for (int X = 0; X < imgTmp.Width; X++)
                    {
                        pPixel[X] = pPixel2[X];
                    }
                    pPixel += bmd.Stride;
                    pPixel2 += bmd2.Stride;
                }
    
                ((Bitmap)ret).UnlockBits(bmd);
                ((Bitmap)imgTmp).UnlockBits(bmd2);
            }
            else
            {
                Graphics gdi = Graphics.FromImage(ret);
                gdi.DrawImageUnscaled(imgTmp, 0, 0);
                gdi.Dispose();
            }
            imgTmp.Dispose(); // just to make sure
        }
        return ret;
    }
    
        5
  •  0
  •   farshad saeidi    8 年前

    以其他方式分享

    try
    {
    var img = Image.FromFile(s);
    var bmp = new Bitmap(img);
    img.Dispose();
    File.Delete(s);
    }
    catch { }