图像编辑和保存内存不足异常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
首先,当你创建一个流时,你需要在完成它后处理它(注意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="<" Click="Back_Click"/>
<Button DockPanel.Dock="Right" Content=">" 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类