从文件中加载图片,不占用文件句柄,不缓存图像,一次性使用,方便回收内存方法

一般人将文件转图片使用以下方式:

/// <summary>
/// 文件转图片(不占用文件句柄)
/// </summary>
/// <param name="filePath">文件路径</param>
/// <param name="toWidth">生成图像宽度</param>
/// <param name="toHeight">生成图像高度</param>
/// <returns></returns>
public static BitmapImage ToBitmapImage(String filePath, Int32? toWidth = null, Int32? toHeight = null)
{
    if (!File.Exists(filePath))
        return null
    var bmp = new BitmapImage()
    bmp.BeginInit();
    bmp.CacheOption = BitmapCacheOption.OnLoad;
    bmp.CreateOptions = BitmapCreateOptions.PreservePixelFormat
    if (toWidth.HasValue)
        bmp.DecodePixelWidth = toWidth.Value;
    if (toHeight.HasValue)
        bmp.DecodePixelHeight = toHeight.Value
    using (var ms = new MemoryStream(File.ReadAllBytes(filePath)))
    {
        bmp.StreamSource = ms;
        bmp.EndInit();
        bmp.Freeze();
    
    return bmp;
}

这里使用了OnLoad图像缓存方式,如果要使用None,那就不能释放MemoryStream,否则图像无法显示;这个缓存也不知道存到什么时候,为了能及时释放内存,就不能使用缓存,MemoryStream对象先作为变量存起来,等到Image.Unloaded触发时再销毁,代码如下:

public sealed class FileImageBehavior : Behavior<Image> {
    #region 依赖属性

    public static readonly DependencyProperty FilePathProperty = DependencyProperty.Register ("FilePath", typeof (String), typeof (FileImageBehavior), new PropertyMetadata (OnFilePathPropertyChanged));

    private static void OnFilePathPropertyChanged (DependencyObject d, DependencyPropertyChangedEventArgs e) {
        var obj = (FileImageBehavior) d;

        if (obj.AssociatedObject != null && obj.AssociatedObject.IsLoaded)
            obj.OnFilePathChanged ((String) e.NewValue);
    }

    /// <summary>
    /// 文件路径
    /// </summary>
    public String FilePath { get => (String) this.GetValue (FilePathProperty); set => this.SetValue (FilePathProperty, value); }

    #endregion

    #region 私有方法

    protected override void OnAttached () {
        base.OnAttached ();

        this.AssociatedObject.Loaded += OnLoaded;
        this.AssociatedObject.Unloaded += OnUnloaded;
    }

    protected override void OnDetaching () {
        base.OnDetaching ();

        this.AssociatedObject.Loaded -= OnLoaded;
        this.AssociatedObject.Unloaded -= OnUnloaded;
    }

    private void OnLoaded (Object sender, RoutedEventArgs e) {
        OnFilePathChanged (FilePath);
    }

    private void OnUnloaded (Object sender, RoutedEventArgs e) {

        this.Release ();
    }

    private void OnFilePathChanged (String file) {
        this.Release ();

        if (String.IsNullOrEmpty (file) || !File.Exists (file))
            return;

        ms = new MemoryStream (File.ReadAllBytes (file));

        var bmp = new BitmapImage ();

        bmp.BeginInit ();
        bmp.CacheOption = BitmapCacheOption.None;
        bmp.CreateOptions = BitmapCreateOptions.PreservePixelFormat;

        if (RenderWidth.HasValue)
            bmp.DecodePixelWidth = RenderWidth.Value; // 设置图像解码后的宽度
        if (RenderHeight.HasValue)
            bmp.DecodePixelHeight = RenderHeight.Value; // 设置图像解码后的高度

        bmp.StreamSource = ms;
        bmp.EndInit ();
        bmp.Freeze ();

        this.AssociatedObject.Source = bmp;
    }

    private void Release () {
        this.AssociatedObject.Source = null;

        if (ms != null) {
            ms.Close ();
            ms.Dispose ();
            ms = null;
        }
    }

    #endregion

    #region 属性

    /// <summary>
    /// 显示宽度
    /// </summary>
    public Int32? RenderWidth { get; set; }
    /// <summary>
    /// 显示高度
    /// </summary>
    public Int32? RenderHeight { get; set; }

    #endregion

    #region 字段

    private MemoryStream ms;

    #endregion
}

使用方式:

<Image Stretch="Uniform">
    <i:Interaction.Behaviors>
        <wl:FileImageBehavior FilePath="{Binding FilePath}" RenderWidth="180" />
    </i:Interaction.Behaviors>
</Image>

 

下面是更新,上面的使用方式过于复杂了,我发现MemoryStrean即使不手动Dispose也能释放,不会造成内存泄漏,所以从文件加载图片,使用下面的方法就可以:

/// <summary>
/// 文件转图片(不占用文件句柄)
/// </summary>
/// <param name="filePath">文件路径</param>
/// <returns></returns>
public static BitmapFrame ToBitmapFrame (String filePath) {
    if (!File.Exists (filePath))
        return null;

    var ms = new MemoryStream (File.ReadAllBytes (filePath));

    var bmp = BitmapFrame.Create (ms, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);
    bmp.Freeze ();
    return bmp;
}

/// <summary>
/// 文件转图片(不占用文件句柄)
/// </summary>
/// <param name="filePath">文件路径</param>
/// <param name="toWidth">生成图像宽度</param>
/// <param name="toHeight">生成图像高度</param>
/// <returns></returns>
public static BitmapImage ToBitmapImage (String filePath, Int32? toWidth = null, Int32? toHeight = null) {
    if (!File.Exists (filePath))
        return null;

    var bmp = new BitmapImage ();

    bmp.BeginInit ();
    bmp.CacheOption = BitmapCacheOption.None;
    bmp.CreateOptions = BitmapCreateOptions.PreservePixelFormat;

    if (toWidth.HasValue)
        bmp.DecodePixelWidth = toWidth.Value;
    if (toHeight.HasValue)
        bmp.DecodePixelHeight = toHeight.Value;

    var ms = new MemoryStream (File.ReadAllBytes (filePath));

    bmp.StreamSource = ms;
    bmp.EndInit ();
    bmp.Freeze ();

    return bmp;
}

用转换器封装就是:

public sealed class FilePathToBitmapFrameConverter : IValueConverter {
    public Object Convert (Object value, Type targetType, Object parameter, CultureInfo culture) {
        return ImageHelper.ToBitmapFrame ((String) value);
    }

    public Object ConvertBack (Object value, Type targetType, Object parameter, CultureInfo culture) {
        return Binding.DoNothing;
    }

    private static Lazy<FilePathToBitmapFrameConverter> lazy = new Lazy<FilePathToBitmapFrameConverter> ();
    public static FilePathToBitmapFrameConverter Instance => lazy.Value;
}

public sealed class FilePathToBitmapImageConverter : IValueConverter {
    public Object Convert (Object value, Type targetType, Object parameter, CultureInfo culture) {
        Int32? width = null, height = null;

        if (parameter is String args) {
            var maps = args.Split ('|');

            foreach (var map in maps) {
                var kv = map.Split (':');

                if (kv.Length != 2)
                    continue;

                if ("w".Equals (kv[0], StringComparison.OrdinalIgnoreCase))
                    width = Int32.Parse (kv[1]);
                else if ("h".Equals (kv[0], StringComparison.OrdinalIgnoreCase))
                    height = Int32.Parse (kv[1]);
            }
        }

        return ImageHelper.ToBitmapImage ((String) value, width, height);
    }

    public Object ConvertBack (Object value, Type targetType, Object parameter, CultureInfo culture) {
        return Binding.DoNothing;
    }

    private static Lazy<FilePathToBitmapImageConverter> lazy = new Lazy<FilePathToBitmapImageConverter> ();
    public static FilePathToBitmapImageConverter Instance => lazy.Value;
}

使用起来就是:

<Image Source="{Binding FilePath,Converter={x:Static wl:FilePathToBitmapImageConverter.Instance},ConverterParameter='w:200'}" />
<Image Source="{Binding FilePath,Converter={x:Static wl:FilePathToBitmapFrameConverter.Instance}}" />

 

posted @ 2020-08-31 10:37  孤独成派  阅读(447)  评论(0编辑  收藏  举报