图像文件复制正在被另一个进程使用。

6

我正在尝试创建一个用户资料编辑窗口,在这个窗口中有一个图像控件。
当我选择一个图像文件时,它会显示在这个图像控件中,并将此文件复制到我的图像文件夹中,第一次没问题,但第二次出现了错误

"The process cannot access the file 'C:\1.jpg' because it is being used by another process."

我认为这是因为我的图像控件正在使用此文件,所以我不知道该怎么办。

private void Select_Click(object sender, RoutedEventArgs e)
{
    OpenFileDialog od = new OpenFileDialog();
    if (od.ShowDialog() == true)
    {
        string imageLocal = @"C:/1.jpg";
        File.Copy(od.FileName, imageLocal, true);
        image1.Source = new BitmapImage(new Uri(imageLocal));
    }
}

问题在于你加载文件并将其转换为图像的方式。使用你目前使用的构造方法是行不通的。如果你想避免“被另一个进程使用”的异常,有其他的方法可供选择。 - Gayot Fow
我能为解决这个问题做些什么? - Lai32290
3个回答

6
如果您想加载和显示图像,并使文件适应于文件系统中的操作(例如重新加载或将其移动到另一个目录),则Uri构造函数将无法工作,因为(正如您指出的那样),BitmapImage类会挂起文件句柄。相反,请使用以下方法...
    private static BitmapImage ByStream(FileInfo info)
    {   //http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/dee7cb68-aca3-402b-b159-2de933f933f1
        try
        {
            if (info.Exists)
            {
                // do this so that the image file can be moved in the file system
                BitmapImage result = new BitmapImage();
                // Create new BitmapImage   
                Stream stream = new MemoryStream(); // Create new MemoryStream   
                Bitmap bitmap = new Bitmap(info.FullName);
                // Create new Bitmap (System.Drawing.Bitmap) from the existing image file 
                                             (albumArtSource set to its path name)   
                bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
                // Save the loaded Bitmap into the MemoryStream - Png format was the only one I 
                              tried that didn't cause an error (tried Jpg, Bmp, MemoryBmp)   
                bitmap.Dispose(); // Dispose bitmap so it releases the source image file   
                result.BeginInit(); // Begin the BitmapImage's initialisation   
                result.StreamSource = stream;
                // Set the BitmapImage's StreamSource to the MemoryStream containing the image   
                result.EndInit(); // End the BitmapImage's initialisation   
                return result; // Finally, set the WPF Image component's source to the 
                                BitmapImage  
            }
            return null;
        }
        catch
        {
            return null;
        }
    }

这个方法接收一个FileInfo对象并返回一个BitmapImage对象,您可以将其显示并同时移动到另一个目录或再次显示它。

下面是来自另一个答案的一个更简单的方法:

public static BitmapImage LoadBitmapImage(string fileName)
{
    using (var stream = new FileStream(fileName, FileMode.Open))
    {
        var bitmapImage = new BitmapImage();
        bitmapImage.BeginInit();
        bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
        bitmapImage.StreamSource = stream;
        bitmapImage.EndInit();
        bitmapImage.Freeze(); 
        return bitmapImage;
    }
}

请尝试使用未损坏的文件进行操作。 - Gayot Fow
PNG编码器也可处理未损坏的JPG文件。所有类型都能准确地显示在屏幕上。而在核心问题方面,您不再会收到异常了吗? - Gayot Fow
@Lai32290 不需要显式选择解码器。您可以将文件内容作为流传递给BitmapImage的“StreamSource”属性。WPF将自动选择正确的解码器,如我的答案所示。此外,此处显示的解决方案涉及System.Drawing.Bitmap,而不是WPF而是WinForms。它将图像文件解码为位图,然后将其重新编码为MemoryStream,最后将其解码为BitmapImage。与直接将FileStream解码为BitmapImage相比,这非常低效。 - Clemens
@Clemen,当然可以。但请公平地理解,这种方法已经在生产实现中使用了数年,并且处理了数十万张图像,没有内存泄漏或其他故障。它所做的概念设计最初是由微软的一位权威人士提出的。不过,如果您觉得有用的话,我很乐意将您的解决方案放到同样的压力场景中,并重新审视此页面的结果。 - Gayot Fow
@Garry 那太棒了!当然,如果有一些隐含的原因可以 更高效 或更稳定地对图像进行中间解码和编码,那么这会很有趣。否则,我只是期望在这里看到“WPF方式”。 - Clemens
显示剩余10条评论

2
下面的方法从文件中加载BitmapImage,加载完成后立即关闭文件。请注意,在源流在EndInit之后立即关闭时,必须设置BitmapCacheOption.OnLoad标志。
public static BitmapImage LoadBitmapImage(string fileName)
{
    using (var stream = new FileStream(fileName, FileMode.Open))
    {
        var bitmapImage = new BitmapImage();
        bitmapImage.BeginInit();
        bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
        bitmapImage.StreamSource = stream;
        bitmapImage.EndInit();
        bitmapImage.Freeze(); // just in case you want to load the image in another thread
        return bitmapImage;
    }
}

这段代码适用于WPF支持的任何图片格式。将图像文件内容作为流传递给StreamSource属性时,WPF会自动创建适当的解码器。


0
非常简单的解决方案:
System.GC.Collect();  
System.GC.WaitForPendingFinalizers();

File.Copy(od.FileName, imageLocal, true);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接