图像编辑和保存内存不足异常C#

本文关键字:异常 内存不足 保存 编辑 图像 | 更新日期: 2023-09-27 17:57:50

我在一个WPF应用程序中工作,在那里我在两个地方显示我的图像,这意味着同一个图像将在两个位置加载。在其中一个位置,图像将与其他几个图像一起显示在滑块中,在滑块中可以编辑和保存。如果该位置没有可用的图像,我应该显示一个单独的图像找不到图像,该图像不可编辑。

当我开始处理该功能时,我在编辑和保存过程中得到了另一个进程使用的异常。因此,在搜索后,我想出了一个解决方案,现在,当我单击滑块中的"下一个"、"上一个"或"第一个"或最后一个时,我很少会出现内存不足异常。滑块只是一个带有4个按钮的图像控件。单击按钮时,将调用以下方法。我不确定是否有内存泄漏。

bool NoImage = true;
private static readonly object _syncRoot = new object();
private BitmapSource LoadImage(string path)
{
    lock (_syncRoot) //lock the object so it doesn't get executed more than once at a time.
    {
        BitmapDecoder decoder = null;
        try
        {
            //If the image is not found in the folder, then show the image not found.
            if (!File.Exists(path) && (path != null))
            {
                System.Drawing.Bitmap ss = XXXX.Resources.ImageNotFound;
                var stream = new System.IO.MemoryStream();
                if (!File.Exists(Path.GetTempPath() + "ImageNotFound.jpg"))
                {
                    FileStream file = new FileStream(Path.GetTempPath() + "ImageNotFound.jpg", FileMode.Create, FileAccess.Write);
                    ss.Save(stream, ImageFormat.Jpeg);
                    stream.Position = 0;
                    stream.WriteTo(file);
                    file.Close();
                    stream.Close();
                }
                path = Path.Combine(Path.GetTempPath(), "ImageNotFound.jpg");
                NoImage = false;
            }
            else
            {
                if (!EnableForEdit)
                    NoImage = false;
                else
                    NoImage = true;
            }
            if (!string.IsNullOrEmpty(path) && (!NoImage || File.Exists(path)))
            {
                using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read))
                {
                    decoder = BitmapDecoder.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
                }
                return decoder.Frames.FirstOrDefault();
            }
            else
                return null;
        }
        catch (OutOfMemoryException ex)
        {
            MessageBox.Show("Insufficient memory to handle the process. Please try again later.", "Application alert");                  
            return null;
        }
        catch (Exception ex)
        {
            // Error handling.
            throw new ApplicationException(ex.Message);
        }
        finally
        {
            decoder = null;
            GC.WaitForFullGCComplete(1000);
            GC.Collect(0, GCCollectionMode.Forced);
        }
    }
}

<Image x:Name="viewImage" Grid.Row="2" Height="100" Width="135" Source="{Binding DisplayImage, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}" />

如果我的方法错误,请告诉我应该在哪里进行更改,或者是否有更简单的方法。请提供帮助。

注意:加载的图像超过5Mb

图像编辑和保存内存不足异常C#

首先,当你创建一个流时,你需要在完成它后处理它(注意Close不会处理,但dispose会关闭),如果没有,那么流就会留在内存消耗资源中

所以你的代码应该如下

using(var stream = new System.IO.MemoryStream())
{
    if (!File.Exists(Path.GetTempPath() + "ImageNotFound.jpg"))
    {
        using(FileStream file = new FileStream(Path.GetTempPath() + "ImageNotFound.jpg", FileMode.Create, FileAccess.Write))
        {
            ss.Save(stream, ImageFormat.Jpeg);
            stream.Position = 0;
            stream.WriteTo(file);
        }
    }
}

第二,你需要减少你的应用程序内存影响

要做到这一点,我建议利用WPF中已经存在的功能,这里有一个快速的例子来说明如何进行

您的型号

public class ImageItem
{
    public Uri URI{ get; set; }
    private BitmapSource _Source;
    public BitmapSource Source
    {
        get
        {
            try
            {
                if (_Source == null) _Source = new BitmapImage(URI);//lazy loading
            }
            catch (Exception)
            {
                _Source = null;
            }
            return _Source;
        }
    }
    public void Save(string filename)
    {
        var img = BitmapFrame.Create(Source);
        var encoder = new JpegBitmapEncoder();
        encoder.Frames.Add(img);
        using(var saveStream = System.IO.File.OpenWrite(filename))
            encoder.Save(saveStream)
    }

}

您的视图模型

public class ImageList : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public ObservableCollection<ImageItem> Images { get; } = new ObservableCollection<ImageItem>();
    private int _SelectedIndex;
//  c# >= 6
    public static readonly PropertyChangedEventArgs SelectedIndexProperty = new PropertyChangedEventArgs(nameof(SelectedIndex));
//  c# < 6
//  public static readonly PropertyChangedEventArgs SelectedIndexProperty = new PropertyChangedEventArgs("SelectedIndex");
    public int SelectedIndex
    {
        get { return _SelectedIndex; }
        set
        {
            _SelectedIndex = value;
//          c# >= 6
            PropertyChanged?.Invoke(this, SelectedIndexProperty);
            PropertyChanged?.Invoke(this, CurrentImageProperty);
//          c# < 6
//          var handler = PropertyChanged;
//          if(handler !=null)
//          {
//              handler (this, SelectedIndexProperty);
//              handler (this, CurrentImageProperty);
//          }
        }
    }
//  c# >= 6
    public static readonly PropertyChangedEventArgs CurrentImageProperty = new PropertyChangedEventArgs(nameof(CurrentImage)); 
//  c# < 6
//  public static readonly PropertyChangedEventArgs CurrentImageProperty = new PropertyChangedEventArgs("CurrentImage"); 
    public ImageItem CurrentImage => Images.Count>0 ?  Images[SelectedIndex] : null;
    public void Next()
    {
        if (SelectedIndex < Images.Count - 1)
            SelectedIndex++;
        else
            SelectedIndex = 0;
    }
    public void Back()
    {
        if (SelectedIndex == 0)
            SelectedIndex = Images.Count - 1;
        else
            SelectedIndex--;
    }
    public void Add(string Filename)
    {
        Images.Add(new ImageItem() { URI= new Uri(Filename) });
//      c# >= 6
        PropertyChanged?.Invoke(this, CurrentImageProperty);
//      c# < 6
//      var handler = PropertyChanged;
//      if(handler !=null)
//      {
//          handler (this, CurrentImageProperty);
//      }
    }
}

最后查看

<Window x:Class="ImageDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:ImageDemo"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <BitmapImage x:Key="NotFound" UriSource="C:'...'NotFound.png"/>
    </Window.Resources>
    <Window.DataContext>
        <local:ImageList/>
    </Window.DataContext>
    <DockPanel>
        <Button Content="&lt;" Click="Back_Click"/>
        <Button DockPanel.Dock="Right" Content="&gt;" Click="Next_Click"/>
        <Image Source="{Binding CurrentImage.Source, Mode=OneWay, 
               TargetNullValue={StaticResource NotFound},
               FallbackValue={StaticResource NotFound}}"/>
    </DockPanel>
</Window>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }
    //c# >= 6
    private ImageList list => DataContext as ImageList;
    //c# < 6
    //private ImageList list {get{ return DataContext as ImageList;}}
    private void Next_Click(object sender, RoutedEventArgs e)
    {
        list.Next();
    }
    private void Back_Click(object sender, RoutedEventArgs e)
    {
        list.Back();
    }
}

注意:因为模型与视图是分开的,所以你可以在几个地方显示相同的图像,而根本没有问题

此外,System.Drawing.Bitmap与WPF不兼容,因此应在System.Windows.Media.Imaging 中使用WPF类