MVC 下载文件完成后删除文件 2020年10月22日 NET 在项目中,我们有时候需要导出一些临时生成的文件数据,例如:Excel文件。这些生成的下载文件,我们可能并不想长期保存,需要在下载完成之后立即删除该文件。 在MVC项目中可以这样实现,创建一个自定义过滤器,重写OnResultExecuted方法 ``` csharp public class DeleteFileAttribute : ActionFilterAttribute { public override void OnResultExecuted(ResultExecutedContext filterContext) { filterContext.HttpContext.Response.Flush(); if (filterContext.Result is FilePathResult filePathResult) { System.IO.File.Delete(filePathResult.FileName); } } } ``` Action 中调用 ``` csharp [DeleteFile] [HttpGet] public ActionResult ExportExcel() { ...... return File("filename","text/plain","donwname"); } ```
C# 获取或设置图片像素点的颜色 2020年10月22日 NET 在C#中,获取或者设置图片像素点的颜色,一般用`Bitmap`对象的`GetPixel`方法和`SetPixel`方法来获取像素点和设置像素点,但这两个方法都很慢。 可以使用`BitmapData`类来加快速度。 ## Bitmap类 `Bitmap`对象封装了GDI+中的一个位图,此位图由图形图像及其属性的像素数据组成。该类的主要方法和属性如下: 1. `GetPixel`方法和`SetPixel`方法:获取和设置一个图像的指定像素的颜色 2. `PixelFormat`属性:返回图像的像素格式 3. `Palette`属性:获取和设置图像所使用的颜色调色板 4. `Heigh`和`Width`属性:返回图像的高度和宽度 5. `LockBits`方法和`UnlockBits`方法:分别锁定和解锁系统内存中的位图像素。在基于像素点的图像处理方法中使用`LockBits`和`UnlockBits`是一个很好的方式,这两种方法可以使我们指定像素的范围来控制位图的任意一部分,从而消除了通过循环对位图的像素逐个进行处理,每调用`LockBits`之后都应该调用一次`UnlockBits` ## BitmapData类 `BitmapData`对象指定了位图的属性 1. `Height`属性:被锁定位图的高度 2. `Width`属性:被锁定位图的高度 3. `PixelFormat`属性:数据的实际像素格式 4. `Scan0`属性:被锁定数组的首字节地址,如果整个图像被锁定,则是图像的第一个字节地址 5. `Stride`属性:步幅,也称为扫描宽度  如上图所示,数组的长度并不一定等于图像像素数组的长度,还有一部分未用区域,这涉及到位图的数据结构,系统要保证每行的字节数必须为4的倍数。 ## bpp 像素深度 像素深度是指存储每个像素所用的位数,也用它来度量图像的分辨率。像素深度决定彩色图像的每个像素可能有的颜色数,或者确定灰度图像的每个像素可能有的灰度级数。 例如,一幅彩色图像的每个像素用R,G,B三个分量表示,若每个分量用8位,那么一个像素共用24位表示,就说像素的深度为24,每个像素可以是16 777 216(2的24次方)种颜色中的一种。在这个意义上,往往把像素深度说成是图像深度。表示一个像素的位数越多,它能表达的颜色数目就越多,而它的深度就越深。 ## LockBitmap类 结合`Bitmap`类和`BitmapData`类新建一个`LockBitmap`类,用来方便获取或者设置图片像素点的颜色 ``` csharp public class LockBitmap { private readonly Bitmap _source = null; IntPtr _iptr = IntPtr.Zero; BitmapData _bitmapData = null; public byte[] Pixels { get; set; } public int Depth { get; private set; } public int Width { get; private set; } public int Height { get; private set; } public LockBitmap(Bitmap source) { this._source = source; } /// <summary> /// 锁定位图数据 /// </summary> public void LockBits() { try { // 获取位图的宽和高 Width = _source.Width; Height = _source.Height; // 获取锁定像素点的总数 int pixelCount = Width * Height; // 创建锁定的范围 Rectangle rect = new Rectangle(0, 0, Width, Height); // 获取像素格式大小 Depth = Image.GetPixelFormatSize(_source.PixelFormat); // 检查像素格式 if (Depth != 8 && Depth != 24 && Depth != 32) { throw new ArgumentException("仅支持8,24和32像素位数的图像"); } // 锁定位图并返回位图数据 _bitmapData = _source.LockBits(rect, ImageLockMode.ReadWrite, _source.PixelFormat); // 创建字节数组以复制像素值 int step = Depth / 8; Pixels = new byte[pixelCount * step]; _iptr = _bitmapData.Scan0; // 将数据从指针复制到数组 Marshal.Copy(_iptr, Pixels, 0, Pixels.Length); } catch (Exception ex) { throw ex; } } /// <summary> /// 解锁位图数据 /// </summary> public void UnlockBits() { try { // 将数据从字节数组复制到指针 Marshal.Copy(Pixels, 0, _iptr, Pixels.Length); // 解锁位图数据 _source.UnlockBits(_bitmapData); } catch (Exception ex) { throw ex; } } /// <summary> /// 获取像素点的颜色 /// </summary> /// <param name="x"></param> /// <param name="y"></param> /// <returns></returns> public Color GetPixel(int x, int y) { Color clr = Color.Empty; // 获取颜色组成数量 int cCount = Depth / 8; // 获取指定像素的起始索引 int i = ((y * Width) + x) * cCount; if (i > Pixels.Length - cCount) throw new IndexOutOfRangeException(); if (Depth == 32) // 获得32 bpp红色,绿色,蓝色和Alpha { byte b = Pixels[i]; byte g = Pixels[i + 1]; byte r = Pixels[i + 2]; byte a = Pixels[i + 3]; // a clr = Color.FromArgb(a, r, g, b); } if (Depth == 24) // 获得24 bpp红色,绿色和蓝色 { byte b = Pixels[i]; byte g = Pixels[i + 1]; byte r = Pixels[i + 2]; clr = Color.FromArgb(r, g, b); } if (Depth == 8) // 获得8 bpp { byte c = Pixels[i]; clr = Color.FromArgb(c, c, c); } return clr; } /// <summary> /// 设置像素点颜色 /// </summary> /// <param name="x"></param> /// <param name="y"></param> /// <param name="color"></param> public void SetPixel(int x, int y, Color color) { // 获取颜色组成数量 int cCount = Depth / 8; // 获取指定像素的起始索引 int i = ((y * Width) + x) * cCount; if (Depth == 32) { Pixels[i] = color.B; Pixels[i + 1] = color.G; Pixels[i + 2] = color.R; Pixels[i + 3] = color.A; } if (Depth == 24) { Pixels[i] = color.B; Pixels[i + 1] = color.G; Pixels[i + 2] = color.R; } if (Depth == 8) { Pixels[i] = color.B; } } } ``` ## 实例 ``` csharp using (Image img = Image.FromFile("ImagePath")) { using (Bitmap bmp = new Bitmap(img)) { int totalX = bmp.Width; int totalY = bmp.Height; var lockBitmap = new LockBitmap(bmp); lockBitmap.LockBits(); var pixelColor = lockBitmap.GetPixel(0, 50); lockBitmap.UnlockBits(); } } ``` ## 参考地址 [Work with Bitmaps Faster in C# - CodeProject](https://www.codeproject.com/Tips/240428/Work-with-Bitmaps-Faster-in-Csharp-3) [像素深度_百度百科](https://baike.baidu.com/item/%E5%83%8F%E7%B4%A0%E6%B7%B1%E5%BA%A6/10150486?fromtitle=BPP&fromid=15196141&fr=aladdin) [色彩深度 - 维基百科,自由的百科全书](https://zh.wikipedia.org/wiki/%E8%89%B2%E5%BD%A9%E6%B7%B1%E5%BA%A6)
Queue 队列学习 2020年10月22日 NET [TOC] ## 简介 队列(Queue)代表了一个先进先出的对象集合。当您需要对各项进行先进先出的访问时,则使用队列。当您在列表中添加一项,称为入队,当您从列表中移除一项时,称为出队。在.NET中,要用到队列,需要使用`Queue`对象。 ## 构造函数 | 重载 | 说明 | | - | - | | Queue<T>() | 初始化 Queue<T> 类的新实例,该实例为空并且具有默认初始容量。| | Queue<T>(IEnumerable<T>) | 初始化 Queue<T> 类的新实例,该实例包含从指定集合复制的元素并且具有足够的容量来容纳所复制的元素。| | Queue<T>(Int32) | 初始化 Queue<T> 类的新实例,该实例为空并且具有指定的初始容量 | ## 属性 | 属性名 | 说明 | | - | - | | Count | Queue<T> 中包含的元素数 | ## 方法 | 方法名 | 说明 | | - | - | | `public void Enqueue (T item);` | 添加对象到队列,提供一个参数,即要添加到队列的对象 | | `public T Dequeue ();` | 移除并返回位于 Queue<T> 开始处的对象 | | `public T Peek ();` | 位于 Queue<T> 的开头的对象 | | `public void TrimExcess ();` | 如果列表为在多个容量的 90%。 这样可以避免产生相对较小增益的大型的重新分配费用。 | | `public void Clear ();` | 从 Queue<T> 中移除所有对象。 | | `public bool Contains (T item);` | 确定某元素是否在 Queue<T> 中。 | | `public T[] ToArray ();` | 将 Queue<T> 元素复制到新数组。 | ## 示例 ``` csharp Queue<string> queue = new Queue<string>(); queue.Enqueue("123"); //向队列插入项 queue.Enqueue("456"); while (queue.Count > 0) { string item1 = queue.Peek(); //获取队列最先插入的项,不删除 string item2 = queue.Dequeue(); //获取队列最先插入的项,并删除 } queue.TrimExcess(); //重置队列容量 ``` ### 说明 * Queue 的容量是 Queue 可以保存的元素数,Queue 的默认初始容量为 32,向 Queue 添加元素时,将通过重新分配来根据需要自动增大容量。可通过调用 `TrimExcess` 来重置容量。因为看源码可以看出`Dequeue`方法获取项的时候,并不会删除该项,而是设置为空。代码如下: ``` csharp /// <summary> /// 移除并返回位于 <see cref="T:System.Collections.Generic.Queue`1" /> 开始处的对象。 /// </summary> /// <returns> /// 从 <see cref="T:System.Collections.Generic.Queue`1" /> 的开始处移除的对象。 /// </returns> /// <exception cref="T:System.InvalidOperationException"> /// <see cref="T:System.Collections.Generic.Queue`1" /> 为空。 /// </exception> [__DynamicallyInvokable] public T Dequeue() { if (this._size == 0) ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EmptyQueue); T obj = this._array[this._head]; //取出开始处的对象 this._array[this._head] = default (T); //设置为空 this._head = (this._head + 1) % this._array.Length; --this._size; ++this._version; return obj; } ``` * 用`Dequeue`方法获取项的时候,如果`Count`属性为零,则会引发`InvalidOperationException`异常。 * 等比因子是当需要更大容量时当前容量要乘以的数字。在构造 Queue 时确定增长因子。默认增长因子为 2.0。 ## 线程安全 `Queue<T>`对象无法保证线程安全,它和普通集合`List<T>`一样存在并发问题,测试代码: ``` csharp static Queue<string> queue = new Queue<string>(50000); protected void Page_Load(object sender, EventArgs e) { Task t1 = new TaskFactory().StartNew(RuDui); Task t2 = new Task(RuDui); t2.Start(); Task t3 = Task.Factory.StartNew(RuDui); Task t4 = Task.Factory.StartNew(RuDui); Task.WaitAll(t1, t2, t3, t4); int count = queue.Count; //返回的数量,不一定是40000 } public static void RuDui() //定义一个入队方法 先进先出 { for (int i = 1; i < 10001; i++) { queue.Enqueue("" + i); } } ``` 要解决这个问题,可以用`ConcurrentQueue<T>`对象 ``` csharp static ConcurrentQueue<string> queue = new ConcurrentQueue<string>(); protected void Page_Load(object sender, EventArgs e) { Task t1 = new TaskFactory().StartNew(RuDui); Task t2 = new Task(RuDui); t2.Start(); Task t3 = Task.Factory.StartNew(RuDui); Task t4 = Task.Factory.StartNew(RuDui); Task.WaitAll(t1, t2, t3, t4); int count = queue.Count; //肯定是40000 } public static void RuDui() //定义一个入队方法 先进先出 { for (int i = 1; i < 10001; i++) { queue.Enqueue("" + i); } } ``` ## 参考文档 [Queue<T> Constructor (System.Collections.Generic) | Microsoft Docs](https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.generic.queue-1.-ctor?view=netframework-4.0) [ConcurrentQueue<T> Class (System.Collections.Concurrent) | Microsoft Docs](https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentqueue-1?view=netframework-4.0)