C# 温故而知新:Stream篇(五)下

时间:2022-05-07
本文章向大家介绍C# 温故而知新:Stream篇(五)下,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

对于重写的方法这里不再重复说明,大家可以参考我写的第一篇

以下是memoryStream独有的方法

virtual byte[] GetBuffer()

这个方法使用时需要小心,因为这个方法返回无符号字节数组,也就是说,即使我只输入几个字符例如”HellowWorld”我们只希望返回11个数据就行,

可是这个方法会把整个缓冲区的数据,包括那些已经分配但是实际上没有用到的字节数据都返回出来,如果想启用这个方法那必须使用上面最后一个构

造函数,将publiclyVisible属性设置成true就行,这也是上面那个构造函数的作用所在

virtual void WriteTo(Stream stream)

这个方法的目的其实在本文开始时讨论性能问题时已经指出,memoryStream常用起中间流的作用,

所以读写在处理完后将内存流写入其他流中

简单示例 XmlWriter中使用MemoryStream

/// <summary>
        /// 演示在xmlWriter中使用MemoryStream
        /// </summary>
        public static void UseMemoryStreamInXMLWriter()
        {
            MemoryStream ms = new MemoryStream();
            using (ms)
            {
                //定义一个XMLWriter
                using (XmlWriter writer = XmlWriter.Create(ms))
                {
                    //写入xml头
                    writer.WriteStartDocument(true);
                    //写入一个元素
                    writer.WriteStartElement("Content");
                    //为这个元素新增一个test属性
                    writer.WriteStartAttribute("test");
                    //设置test属性的值
                    writer.WriteValue("逆时针的风");
                    //释放缓冲,这里可以不用释放,但是在实际项目中可能要考虑部分释放对性能带来的提升
                    writer.Flush();
                    Console.WriteLine("此时内存使用量为:{2}KB,该MemoryStream的已经使用的容量为{0}byte,默认容量为{1}byte",
                        Math.Round((double)ms.Length, 4), ms.Capacity,GC.GetTotalMemory(false)/1024);
                    Console.WriteLine("重新定位前MemoryStream所在的位置是{0}",ms.Position);
                    //将流中所在的当前位置往后移动7位,相当于空格
                    ms.Seek(7, SeekOrigin.Current);
                    Console.WriteLine("重新定位后MemoryStream所在的位置是{0}", ms.Position);
                    //如果将流所在的位置设置为如下所示的位置则xml文件会被打乱
                    //ms.Position = 0;
                    writer.WriteStartElement("Content2");
                    writer.WriteStartAttribute("testInner");
                    writer.WriteValue("逆时针的风Inner");
                    writer.WriteEndElement();
                    writer.WriteEndElement();
                    //再次释放
                    writer.Flush();
                    Console.WriteLine("此时内存使用量为:{2}KB,该MemoryStream的已经使用的容量为{0}byte,默认容量为{1}byte",
                        Math.Round((double)ms.Length, 4), ms.Capacity, GC.GetTotalMemory(false)/1024);
                    //建立一个FileStream  文件创建目的地是d:test.xml
                    FileStream fs = new FileStream(@"d:test.xml",FileMode.OpenOrCreate);
                    using (fs)
                    {
                        //将内存流注入FileStream
                        ms.WriteTo(fs);
                        if(ms.CanWrite)
                          //释放缓冲区
                        fs.Flush();
                    }
                }
            }
        }

输出结果:

简单示例:自定义一个处理图片的HttpHandler

有时项目里我们必须将图片进行一定的操作,例如水印,下载等,为了方便和管理我们可以自定义一个HttpHander 来负责这些工作

后台:

public class ImageHandler : IHttpHandler
    {
        #region IHttpHandler Members


        public bool IsReusable
        {
            get { return true; }
        }


        /// <summary>
        /// 实现IHTTPHandler后必须实现的方法
        /// </summary>
        /// <param name="context">HttpContext上下文</param>
        public void ProcessRequest(HttpContext context)
        {
            context.Response.Clear();
            //得到图片名
            var imageName = context.Request["ImageName"] == null ? "逆时针的风"
                : context.Request["ImageName"].ToString();
            //得到图片ID,这里只是演示,实际项目中不是这么做的
            var id = context.Request["Id"] == null ? "01"
                : context.Request["Id"].ToString();
            //得到图片地址
            var stringFilePath = context.Server.MapPath(string.Format("~/Image/{0}{1}.jpg", imageName, id));
            //声明一个FileStream用来将图片暂时放入流中
            FileStream stream = new FileStream(stringFilePath, FileMode.Open);
            using (stream)
            {
                //透过GetImageFromStream方法将图片放入byte数组中
                byte[] imageBytes = this.GetImageFromStream(stream,context);
                //上下文确定写到客户短时的文件类型
                context.Response.ContentType = "image/jpeg";
                //上下文将imageBytes中的数据写到前段
                context.Response.BinaryWrite(imageBytes);
                stream.Close();
            }
        }


        /// <summary>
        /// 将流中的图片信息放入byte数组后返回该数组
        /// </summary>
        /// <param name="stream">文件流</param>
        /// <param name="context">上下文</param>
        /// <returns></returns>
        private byte[] GetImageFromStream(FileStream stream, HttpContext context)
        {
            //通过stream得到Image
            Image image = Image.FromStream(stream);
            //加上水印
            image = SetWaterImage(image, context);
            //得到一个ms对象
            MemoryStream ms = new MemoryStream();
            using (ms)
            {
               //将图片保存至内存流
                image.Save(ms, ImageFormat.Jpeg);
                byte[] imageBytes = new byte[ms.Length];
                ms.Position = 0;
                //通过内存流读取到imageBytes
                ms.Read(imageBytes, 0, imageBytes.Length);
                ms.Close();
                //返回imageBytes
                return imageBytes;
            }
        }
        /// <summary>
        /// 为图片加上水印,这个方法不用在意,只是演示,所以没加透明度
        /// 下次再加上吧
        /// </summary>
        /// <param name="image">需要加水印的图片</param>
        /// <param name="context">上下文</param>
        /// <returns></returns>
        private Image SetWaterImage(Image image,HttpContext context) 
        {
            Graphics graphics = Graphics.FromImage(image);
            Image waterImage = Image.FromFile(context.Server.MapPath("~/Image/逆时针的风01.jpg"));
            //在大图右下角画上水印图就行
            graphics.DrawImage(waterImage,
                new Point { 
                    X = image.Size.Width - waterImage.Size.Width,
                    Y = image.Size.Height - waterImage.Size.Height 
                });
            return image;
        }


        #endregion
    }

别忘了还要在Web.Config中进行配置,别忘记verb和path属性,否则会报错

    <httpHandlers>
      <add type="ImageHandler.ImageHandler,ImageHandler"  verb="*" path="ImageHandler.apsx"/>
    </httpHandlers>

这样前台便能使用了

<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
    <h2>
        About    </h2>
    <p>
        Put content here.        <asp:Image runat="server" ImageUrl="ImageHandler.apsx?ImageName=逆时针的风&Id=02" />
    </p></asp:Content>

输出结果

本章总结

本章主要介绍了MemoryStream 的一些概念,异常,结构,包括如何使用,如何解决一些异常等