zoukankan      html  css  js  c++  java
  • 从文件中加载图片,不占用文件句柄,不缓存图像,一次性使用,方便回收内存方法

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

    /// <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}}" />
  • 相关阅读:
    mysql 设置密码
    linux 下如何抓取HTTP流量包(httpry)
    m2a-vm超频的方法
    生产服务器环境最小化安装后 Centos 6.5优化配置备忘
    CentOS关闭休眠和屏保模式
    微信公众平台开发教程第2篇-----微信开发者接入
    微信公众平台开发教程第1篇-新手解惑
    android 文件读取(assets、raw)
    员工培训的七大误区和三个内核价值
    从业务专家进阶到管理者
  • 原文地址:https://www.cnblogs.com/pumbaa/p/13588305.html
Copyright © 2011-2022 走看看