c# IOSerialize 验证码、图片缩放

文件流的读写

文件写入方式:

        { 
                DirectoryInfo directory = new DirectoryInfo(LogPath);//不存在不报错  注意exists属性
                Console.WriteLine(string.Format("{0} {1} {2}", directory.FullName, directory.CreationTime, directory.LastWriteTime));
                string fileName = Path.Combine(LogPath, "log.txt");
                string fileNameCopy = Path.Combine(LogPath, "logCopy.txt");
                string fileNameMove = Path.Combine(LogPath, "logMove.txt");
                bool isExists = File.Exists(fileName);
                if (!isExists)
                {
                    Directory.CreateDirectory(LogPath);//创建了文件夹之后,才能创建里面的文件
                    using (FileStream fileStream = File.Create(fileName))//打开文件流 (创建文件并写入)
                    {
                        string name = "12345567778890";
                        byte[] bytes = Encoding.Default.GetBytes(name);
                        fileStream.Write(bytes, 0, bytes.Length);
                        //数据是先被读到了内存中的 ,再把数据写到文件中,当你数据读完的时候不代表你的数据已经写完了,
                        //因为还有一部分有可能会留在内存这个缓冲区中。这时候如果你调用了close()方法关闭了读写流,
                        //那么这部分数据就会丢失,所以为了防止数据丢失,应该在关闭读写流之前先flush()。
                        fileStream.Flush();
                    }
                    // 如果文件不存在就创建,如果存在就新建一个覆盖掉
                    using (FileStream fileStream = File.Create(fileName))//打开文件流 (创建文件并写入)
                    {
                        StreamWriter sw = new StreamWriter(fileStream);
                        //直接通过StreamWriter对象的WriteLine方法写入数据
                        sw.WriteLine("1234567890");
                        sw.Flush();
                    }
                    //追加内容
                    using (StreamWriter sw = File.AppendText(fileName))//流写入器(创建/打开文件并写入)
                    {
                        string msg = "今天是Course6IOSerialize,今天上课的人有55个人";
                        sw.WriteLine(msg);
                        sw.Flush();
                    }
                    using (StreamWriter sw = File.AppendText(fileName))//流写入器(创建/打开文件并写入)
                    {
                        //二进制的写法
                        string name = "0987654321";
                        byte[] bytes = Encoding.Default.GetBytes(name);
                        sw.BaseStream.Write(bytes, 0, bytes.Length);
                        sw.Flush();
                    }

                 File.Copy(fileName, fileNameCopy);
                 File.Move(fileName, fileNameMove);
                 File.Delete(fileNameCopy);
                 File.Delete(fileNameMove);//尽量不要delete

                }
            }

 

 

文件读取方式

{
                     #region 文件太大,分批读取

                    //文件太大,用工具去打开的时候,计算机直接卡死了
                    using (FileStream stream = File.OpenRead(fileName))//分批读取
                    {
                        int length = 1024*1024;//1M
                        int result = 0;
                        //do-while循环,先读取一部分长度的,如果实际读出来的和假定读取的长度一样,说明还未读完或刚好读完,继续读取然后再判断
                        do
                        {
                            byte[] bytes = new byte[length];
                            result = stream.Read(bytes, 0, length);
                            for (int i = 0; i < result; i++)
                            {
                                Console.WriteLine(bytes[i].ToString());
                            }
                        }
                        while (length == result);
                    }
                    #endregion
              }     

                { 
                //读取硬盘信息
                DriveInfo[] drives = DriveInfo.GetDrives();

                foreach (DriveInfo drive in drives)
                {
                    if (drive.IsReady)
                        Console.WriteLine("类型:{0} 卷标:{1} 名称:{2} 总空间:{3} 剩余空间:{4}",
drive.DriveType, drive.VolumeLabel, drive.Name, drive.TotalSize, drive.TotalFreeSpace);
else Console.WriteLine("类型:{0} is not ready", drive.DriveType); } }

换一种方式:

 

     public static void BigFile()
        {
            List<byte> list = new List<byte>();
            #region 文件太大,分批读取 
            //文件太大,用工具去打开的时候,计算机直接卡死了
            using (FileStream stream = File.OpenRead(@"D:\迅雷下载\【朝夕教育】2021春招Kafka高频面试题.pdf"))//分批读取
            {
                //获取包含扩展名的文件名 
                string fileName = stream.Name.Substring(stream.Name.LastIndexOf("\\") + 1);  
                //获取到文件的后缀名
                string aLastName = stream.Name.Substring(stream.Name.LastIndexOf(".") + 1);
                int length = 1024 * 1024;
                int result = 0;
                //do-while循环,先读取一部分长度的,如果实际读出来的和假定读取的长度一样,说明还未读完或刚好读完,继续读取然后再判断
                do
                {
                    byte[] bytes = new byte[length];
                    //result是实际读取到的字节数
                    result = stream.Read(bytes, 0, length);
                    for (int i = 0; i < result - 1; i++)
                    {
                        list.Add(bytes[i]);
                    }

                }
                while (length == result);
            }

            byte[] test = list.ToArray();
            using (FileStream fs = new FileStream($@"D:\迅雷下载\text.pdf", FileMode.Create))
            { 
                fs.Write(test, 0, test.Length);
                fs.Flush();
            } 
            #endregion
        }

 

 

其他一些使用方式

           { 
                Console.WriteLine(Path.GetDirectoryName(@"d:\\abc")); //将返回 d:\
                Console.WriteLine(Path.GetDirectoryName(@"d:\\abc\"));// 将返回 d:\abc
                Console.WriteLine(Path.GetRandomFileName());//将返回随机的文件名
                Console.WriteLine(Path.GetFileNameWithoutExtension("d:\\abc.txt"));// 将返回abc
                Console.WriteLine(Path.GetInvalidPathChars());// 将返回禁止在路径中使用的字符
                Console.WriteLine(Path.GetInvalidFileNameChars());//将返回禁止在文件名中使用的字符
                Console.WriteLine(Path.Combine("d:\\abc\\", "log.txt"));//合并两个路径,结果:d:\\abc\\log.txt
            }

自定义的日志 

      public static void Log(string msg)
        {
            StreamWriter sw = null;
            try
            {
                string fileName = "log.txt";
                string totalPath = Path.Combine(LogPath, fileName);

                if (!Directory.Exists(LogPath))
                {
                    Directory.CreateDirectory(LogPath);
                }
                sw = File.AppendText(totalPath); // 如果文件不存在,就新建一个文件,然后写入内容,存在直接追加内容 这里其实和上面的if判断重复了,功能一样。
                sw.WriteLine(string.Format("{0}:{1}", DateTime.Now, msg));
                sw.WriteLine("***************************************************");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);//log //建议大家在写日志的时候,把异常吞掉;
                //throw ex;
                //throw new exception("这里异常");
            }
            finally//无论是否发生异常  全都都会执行
            {
                if (sw != null)
                {
                    sw.Flush();
                    sw.Close();
                    sw.Dispose();
                }
            }
        }

 

找出全部的子文件夹,递归实现
递归:可以理解为类型,或者说是一种编程方式

      /// <summary>
        /// 找出全部的子文件夹
        /// 递归:可以理解为类型,或者说是一种编程方式
        /// 
        /// 获取“D:\软谋教育\Git_Work”路径下的所有文件夹
        /// 
        /// 1、在递归的时候,计算机是在高强度的计算;
        /// 2、一定要有跳出循环的判断,避免死循环;
        /// 3、在使用递归的时候,尽量避免多线程;
        /// </summary>
        /// <param name="rootPath">根目录</param>
        /// <returns></returns>
        public static List<DirectoryInfo> GetAllDirectory(string rootPath)
        {
            if (!Directory.Exists(rootPath))
                return new List<DirectoryInfo>();

            //一个存储路径信息的容器
            List<DirectoryInfo> directoryList = new List<DirectoryInfo>();//容器 
            DirectoryInfo directory = new DirectoryInfo(rootPath);//root文件夹
            directoryList.Add(directory); 
            var directioryList = GetChilds(directoryList, directory); 
            return directioryList;  
        }

        private static List<DirectoryInfo> GetChilds(List<DirectoryInfo> directoryList, DirectoryInfo directory)
        {
            //获取当前目录下的所有子目录
            var chaildArray = directory.GetDirectories();
            if (chaildArray != null && chaildArray.Length > 0)
            {
                foreach (var child in chaildArray)
                {
                    directoryList.Add(child);
                    GetChilds(directoryList, child);
                }
            }
            return directoryList;
        }

 

看下面的一个递归方法:

 
        private void Wait()
        {
            if (DateTime.Now.Millisecond < 999)
            { 
                Wait(); 
            }
            else
                return; 
        }

 

这个递归方法是会出问题的,因为计算机的能力是很强大的,一秒内会执行很多次递归方法,很容易卡死的,还有就是不要在递归中写多线程,这样会疯狂开启很多子线程的。

 序列化

基础类:

namespace IOSerialize
{

    [Serializable]  //必须添加序列化特性
    public class Person
    {
        [NonSerialized]
        public int Id = 1;

        public string Name { get; set; }

        public string Sex { get; set; }
    }

    [Serializable]  //必须添加序列化特性
    public class Programmer : Person
    {
        private string Language { get; set; }//编程语言
        public string Description { get; set; }
    }

    public class DataFactory
    {
        /// <summary>
        /// 初始化数据的
        /// </summary>
        /// <returns></returns>
        public static List<Programmer> BuildProgrammerList()
        {
            #region data prepare
            List<Programmer> list = new List<Programmer>();
            list.Add(new Programmer()
            {
                Id = 1,
                Description="高级班学员",
                Name = "SoWhat",
                Sex = ""
            });
            list.Add(new Programmer()
            {
                Id = 1,
                Description = "高级班学员",
                Name = "day",
                Sex = ""
            });
            list.Add(new Programmer()
            {
                Id = 1,
                Description = "高级班学员",
                Name = "领悟",
                Sex = ""
            });
            list.Add(new Programmer()
            {
                Id = 1,
                Description = "高级班学员",
                Name = "Sam",
                Sex = ""
            });
            list.Add(new Programmer()
            {
                Id = 1,
                Description = "高级班学员",
                Name = "AlphaGo",
                Sex = ""
            });
            list.Add(new Programmer()
            {
                Id = 1,
                Description = "高级班学员",
                Name = "折腾",
                Sex = ""
            });
            list.Add(new Programmer()
            {
                Id = 1,
                Description = "高级班学员",
                Name = "Me860",
                Sex = ""
            });
            list.Add(new Programmer()
            {
                Id = 1,
                Description = "高级班学员",
                Name = "打兔子的猎人",
                Sex = ""
            });
            list.Add(new Programmer()
            {
                Id = 1,
                Description = "高级班学员",
                Name = "Nine",
                Sex = ""
            });
            list.Add(new Programmer()
            {
                Id = 1,
                Description = "高级班学员",
                Name = "",
                Sex = ""
            });
            list.Add(new Programmer()
            {
                Id = 1,
                Description = "高级班学员",
                Name = "微笑刺客",
                Sex = ""
            });
            list.Add(new Programmer()
            {
                Id = 1,
                Description = "高级班学员",
                Name = "waltz",
                Sex = ""
            });
            list.Add(new Programmer()
            {
                Id = 1,
                Description = "高级班学员",
                Name = "爱在昨天",
                Sex = ""
            });
            list.Add(new Programmer()
            {
                Id = 1,
                Description = "高级班学员",
                Name = "waltz",
                Sex = ""
            });
            #endregion

            return list;
        }
    }
}

 

二进制序列化

        /// <summary>
        /// 二进制序列化器
        /// </summary>
        public static void BinarySerialize()
        {
            //使用二进制序列化对象
            string fileName = Path.Combine(Constant.SerializeDataPath, @"BinarySerialize.txt");//文件名称与路径
            using (Stream fStream = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite))
            {//需要一个stream,这里是直接写入文件了
                List<Programmer> pList = DataFactory.BuildProgrammerList();
                BinaryFormatter binFormat = new BinaryFormatter();//创建二进制序列化器
                binFormat.Serialize(fStream, pList);
            }
            using (Stream fStream = new FileStream(fileName, FileMode.Open, FileAccess.ReadWrite))
            {//需要一个stream,这里是来源于文件
                BinaryFormatter binFormat = new BinaryFormatter();//创建二进制序列化器
                //使用二进制反序列化对象
                fStream.Position = 0;//重置流位置
                List<Programmer> pList = (List<Programmer>)binFormat.Deserialize(fStream);//反序列化对象
            }
        }

 

上面代码实现了二进制的序列化与反序列化

SOAP序列化

        /// <summary>
        /// soap序列化器
        /// </summary>
        public static void SoapSerialize()
        {
            //使用Soap序列化对象
            string fileName = Path.Combine(Constant.SerializeDataPath, @"SoapSerialize.txt");//文件名称与路径
            using (Stream fStream = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite))
            {
                List<Programmer> pList = DataFactory.BuildProgrammerList();
                SoapFormatter soapFormat = new SoapFormatter();//创建二进制序列化器
                //soapFormat.Serialize(fStream, list);//SOAP不能序列化泛型对象
                soapFormat.Serialize(fStream, pList.ToArray());
            }
            using (Stream fStream = new FileStream(fileName, FileMode.Open, FileAccess.ReadWrite))
            {
                SoapFormatter soapFormat = new SoapFormatter();//创建二进制序列化器
                //使用二进制反序列化对象
                fStream.Position = 0;//重置流位置
                List<Programmer> pList = ((Programmer[])soapFormat.Deserialize(fStream)).ToList();//反序列化对象
            }
        }

 

需要注意的是SOAP序列化器是不支持泛型对象的。在跨平台传输的时候用的比较多。

Simple Object Access Protocol 简单对象访问协议。是一种轻量的,简单的,基于xml的协议。 

它是一种通信协议,用于应用程序之间的能信,它是一种用于发送消息的格式,被设计用来通过互联网进行通信,它基于xml,独立于平台,语言,它简单且可以扩展,它可以允许绕过防火墙。

可以所它认为是一种协议,同时它也是一种消息发送的格式。

XML序列化器

        /// <summary>
        /// XML序列化器
        /// </summary>
        public static void XmlSerialize()
        {
            //使用XML序列化对象
            string fileName = Path.Combine(@"D:\TEST", @"Student.xml");//文件名称与路径
            using (Stream fStream = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite))
            {
                List<Programmer> pList = DataFactory.BuildProgrammerList();
                XmlSerializer xmlFormat = new XmlSerializer(typeof(List<Programmer>));//创建XML序列化器,需要指定对象的类型
                xmlFormat.Serialize(fStream, pList);
            }
            using (Stream fStream = new FileStream(fileName, FileMode.Open, FileAccess.ReadWrite))
            {
                XmlSerializer xmlFormat = new XmlSerializer(typeof(List<Programmer>));//创建XML序列化器,需要指定对象的类型
                //使用XML反序列化对象
                fStream.Position = 0;//重置流位置
                List<Programmer> pList = pList = (List<Programmer>)xmlFormat.Deserialize(fStream);
            }
        }

 

XML是独立于软件和硬件的信息传输工具。 
目前,XML在Web中起到的作用不会亚于一直作为 Web 基石的 HTML。 
XML无所不在XML是各种应用程序之间进行数据传输的最常用的工具,并且在信息存储和描述领域变得越来越流行。

上面是将数据以xml序列化的形式存在硬盘上,然后访问硬盘中的文件来解析,下面实现直接在内存中进行序列化并且解析:

 

 /// <summary>
    /// 使用序列化器完成的
    /// </summary>
    public class XmlHelper
    {

        /// <summary>
        /// XmlSerializer序列化实体为字符串  不保存到硬盘,只在内存中处理
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <returns></returns>
        public static string ToXml<T>(T t) where T : new()
        {
            XmlSerializer xmlSerializer = new XmlSerializer(t.GetType());
            Stream stream = new MemoryStream();
            xmlSerializer.Serialize(stream, t);
            stream.Position = 0;
            StreamReader reader = new StreamReader(stream);
            string text = reader.ReadToEnd();
            return text;
        }

        /// <summary>
        /// 字符串序列化成XML
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="content"></param>
        /// <returns></returns>
        public static T ToObject<T>(string content) where T : new()
        {
            using (MemoryStream stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(content)))
            {
                XmlSerializer xmlFormat = new XmlSerializer(typeof(T));
                return (T)xmlFormat.Deserialize(stream);
            }
        }

        /// <summary>
        /// 文件反序列化成实体
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public static T FileToObject<T>(string fileName) where T : new()
        {
            string CurrentXMLPath = ConfigurationManager.AppSettings["CurrentXMLPath"];
            fileName = Path.Combine(CurrentXMLPath, @"Student.xml");
            using (Stream fStream = new FileStream(fileName, FileMode.Open, FileAccess.ReadWrite))
            {
                XmlSerializer xmlFormat = new XmlSerializer(typeof(T));
                return (T)xmlFormat.Deserialize(fStream);
            }
        }
    }

 

调用:

                List<Programmer> list = DataFactory.BuildProgrammerList();
                {
                    Console.WriteLine("********************XmlHelper**********************");
                    string xmlResult = XmlHelper.ToXml<List<Programmer>>(list);
                    List<Programmer> list1 = XmlHelper.ToObject<List<Programmer>>(xmlResult);
                    //List<Programmer> list2 = XmlHelper.FileToObject<List<Programmer>>("");
                }

 

验证码

 绘图的原理很简单:Bitmap就像一张画布,Graphics如同画图的手,把Pen或Brush等绘图对象画在Bitmap这张画布上。

下面是封装了2个很简单的生成验证码的方法:

     /// <summary>
        /// 画验证码
        /// </summary>
        public static void Drawing()
        {
            Bitmap bitmapobj = new Bitmap(100, 100);
            //在Bitmap上创建一个新的Graphics对象
            Graphics g = Graphics.FromImage(bitmapobj);
            //创建绘画对象,如Pen,Brush等
            Pen redPen = new Pen(Color.Red, 8);
            g.Clear(Color.White);
            //绘制图形
            g.DrawLine(redPen, 50, 20, 500, 20);
            g.DrawEllipse(Pens.Black, new Rectangle(0, 0, 200, 100));//画椭圆
            g.DrawArc(Pens.Black, new Rectangle(0, 0, 100, 100), 60, 180);//画弧线
            g.DrawLine(Pens.Black, 10, 10, 100, 100);//画直线
            g.DrawRectangle(Pens.Black, new Rectangle(0, 0, 100, 200));//画矩形
            g.DrawString("我爱北京天安门", new Font("微软雅黑", 12), new SolidBrush(Color.Red), new PointF(10, 10));//画字符串
            //g.DrawImage(
            if (!Directory.Exists(ImagePath))
                Directory.CreateDirectory(ImagePath);
            bitmapobj.Save(ImagePath + "pic1.jpg", ImageFormat.Jpeg);
            //释放所有对象
            bitmapobj.Dispose();
            g.Dispose();
        }

        public static void VerificationCode()
        {
            Bitmap bitmapobj = new Bitmap(300, 300);
            //在Bitmap上创建一个新的Graphics对象
            Graphics g = Graphics.FromImage(bitmapobj);
            g.DrawRectangle(Pens.Black, new Rectangle(0, 0, 150, 50));//画矩形
            g.FillRectangle(Brushes.White, new Rectangle(1, 1, 149, 49));
            g.DrawArc(Pens.Blue, new Rectangle(10, 10, 140, 10), 150, 90);//干扰线
            string[] arrStr = new string[] { "", "", "", "", "", "", "", "", "", "" };
            Random r = new Random();
            int i;
            for (int j = 0; j < 4; j++)
            {
                i = r.Next(10);
                g.DrawString(arrStr[i], new Font("微软雅黑", 15), Brushes.Red, new PointF(j * 30, 10));
            }
            bitmapobj.Save(Path.Combine(ImagePath, "Verif.jpg"), ImageFormat.Jpeg);
            bitmapobj.Dispose();
            g.Dispose();
        }

 

 

 

图片压缩

 

        /// <summary>
        /// 按比例缩放,图片不会变形,会优先满足原图和最大长宽比例最高的一项
        /// </summary>
        /// <param name="oldPath"></param>
        /// <param name="newPath"></param>
        /// <param name="maxWidth"></param>
        /// <param name="maxHeight"></param>
        public static void CompressPercent(string oldPath, string newPath, int maxWidth, int maxHeight)
        {
            Image _sourceImg = Image.FromFile(oldPath);
            double _newW = (double)maxWidth;
            double _newH = (double)maxHeight;
            double percentWidth = (double)_sourceImg.Width > maxWidth ? (double)maxWidth : (double)_sourceImg.Width;

            if ((double)_sourceImg.Height * (double)percentWidth / (double)_sourceImg.Width > (double)maxHeight)
            {
                _newH = (double)maxHeight;
                _newW = (double)maxHeight / (double)_sourceImg.Height * (double)_sourceImg.Width;
            }
            else
            {
                _newW = percentWidth;
                _newH = (percentWidth / (double)_sourceImg.Width) * (double)_sourceImg.Height;
            }
            Image bitmap = new Bitmap((int)_newW, (int)_newH);
            Graphics g = Graphics.FromImage(bitmap);
            g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            g.Clear(Color.Transparent);
            g.DrawImage(_sourceImg, new Rectangle(0, 0, (int)_newW, (int)_newH), new Rectangle(0, 0, _sourceImg.Width, _sourceImg.Height), GraphicsUnit.Pixel);
            _sourceImg.Dispose();
            g.Dispose();
            bitmap.Save(newPath, System.Drawing.Imaging.ImageFormat.Jpeg);
            bitmap.Dispose();
        }

        /// <summary>
        /// 按照指定大小对图片进行缩放,可能会图片变形
        /// </summary>
        /// <param name="oldPath"></param>
        /// <param name="newPath"></param>
        /// <param name="newWidth"></param>
        /// <param name="newHeight"></param>
        public static void ImageChangeBySize(string oldPath, string newPath, int newWidth, int newHeight)
        {
            Image sourceImg = Image.FromFile(oldPath);
            System.Drawing.Image bitmap = new System.Drawing.Bitmap(newWidth, newHeight);
            Graphics g = Graphics.FromImage(bitmap);
            g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            g.Clear(Color.Transparent);
            g.DrawImage(sourceImg, new Rectangle(0, 0, newWidth, newHeight), new Rectangle(0, 0, sourceImg.Width, sourceImg.Height), GraphicsUnit.Pixel);
            sourceImg.Dispose();
            g.Dispose();
            bitmap.Save(newPath, System.Drawing.Imaging.ImageFormat.Jpeg);
            bitmap.Dispose();
        }

 

  

 

二维码

 

        /// <summary>  
        /// 生成二维码图片  
        /// </summary>  
        /// <param name="codeNumber">要生成二维码内容的字符串</param>       
        /// <param name="size">大小尺寸</param>  
        /// <returns>二维码图片</returns>  
        public string Create_ImgCode(string codeNumber)
        {
            //创建二维码生成类  
            QRCodeEncoder qrCodeEncoder = new QRCodeEncoder();
            //设置编码模式  
            qrCodeEncoder.QRCodeEncodeMode = QRCodeEncoder.ENCODE_MODE.BYTE;
            //设置编码测量度  
            qrCodeEncoder.QRCodeScale = 10;
            //设置编码版本  
            qrCodeEncoder.QRCodeVersion = 0;
            //设置编码错误纠正  
            qrCodeEncoder.QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.M;
            //生成二维码图片  
            System.Drawing.Bitmap image = qrCodeEncoder.Encode(codeNumber, Encoding.GetEncoding("UTF-8"));
            string filePath = System.Web.HttpContext.Current.Server.MapPath(string.Format("~/QRCode/{0}.jpg", codeNumber));
            image.Save(filePath);
            image.Dispose();
            return string.Format("/QRCode/{0}.jpg", codeNumber);
 
        }

 

xml实际操作的方法

   

  

posted @ 2021-10-04 16:17  安静点--  阅读(77)  评论(0编辑  收藏  举报