C# MVC 自定义ActionResult实现EXCEL下载
前言
在WEB中,經常要使用到將數據轉換成EXCEL,并進行下載。這里整理資料并封裝了一個自定義ActionResult類,便于使用。如果文章對你有幫助,請點個贊。
話不多少,這里轉換EXCEL使用的NPOI。還是用了一下反射的知識,便于識別實體類的一些自定義特性。
?
一、自定義一個Attribute
using System;namespace WebSeat.Entity.Member.Attributes {/// <summary>/// 說明:Excel屬性特性/// 創建日期:2016/12/13 14:24:13/// 創建人:曹永承/// </summary>[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true), Serializable]public class ExcelDataOptionAttribute:Attribute{/// <summary>/// 顯示列下標/// </summary>public ushort ColumnIndex { get; set; }/// <summary>/// 顯示名稱/// </summary>public string DisplayName { get; set; }/// <summary>/// 列寬/// </summary>public int ColumnWidth { get; set; }/// <summary>/// 單元格數據格式/// </summary>public string Formater { get; set; }}}該Attribute用于標記到實體對象的屬性上,后面通過反射來識別具體的值
?
二、定義一個實體類
using WebSeat.Entity.Member.Attributes;namespace WebSeat.Entity.Member.Excel {/// <summary>/// 說明:市首頁數據統計Excel表格樣式/// 創建日期:2016/12/13 14:19:27/// 創建人:曹永承/// </summary>public class CityStatics{[ExcelDataOption(ColumnIndex = 0, DisplayName = "時段",Formater ="@", ColumnWidth = 14)]public string DataDuring { get; set; }[ExcelDataOption(ColumnIndex =1,DisplayName ="城市",ColumnWidth =14)]public string City { get; set; }[ExcelDataOption(ColumnIndex = 2, DisplayName = "登錄人數", ColumnWidth = 12)]public int StudentLoginedCount { get; set; }[ExcelDataOption(ColumnIndex = 3,DisplayName ="登錄次數", ColumnWidth = 12)]public int StudentLoginTimes { get; set; }[ExcelDataOption(ColumnIndex = 4,DisplayName ="登錄率",Formater ="0.00%", ColumnWidth = 12)]public decimal StudentLoginRatio { get; set; }[ExcelDataOption(ColumnIndex = 5,DisplayName ="學習節數", ColumnWidth = 12)]public int StudyPeriod { get; set; }[ExcelDataOption(ColumnIndex = 6, DisplayName = "學習次數", ColumnWidth = 12)]public int StudyTimes { get; set; }[ExcelDataOption(ColumnIndex = 7, DisplayName = "人均學習節數(節/人)", Formater = "0.00", ColumnWidth =23)]public decimal StudyRatio { get; set; }[ExcelDataOption(ColumnIndex = 8, DisplayName = "轉化率",Formater = "0.00%", ColumnWidth = 12)]public decimal StudyConvertRatio { get; set; }} }注意:如果屬性沒有標注ExcelDataOption特性,那么該屬性是不會導出到EXCEL中。
ExcelDataOption中Formater屬性,是設置單元格數據類型,這里對于excel中單元格的數據顯示個數,例如上面"0.00%"表示以百分百的形式顯示數字,且保留2位有效小數
?
三、定義一個Excel導出父類
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Web; using System.Web.Mvc; using WebSeat.Site.Member.Helper;namespace WebSeat.Site.Member.CustomResult {/// <summary>/// 說明:導出Excel/// 創建日期:2016/12/13 13:12:37/// 創建人:曹永承/// </summary>public abstract class ExcelBaseResult<T> :ActionResult{#region 屬性/// <summary>/// 數據實體/// </summary>public IList<T> Entity { get; set; }/// <summary>/// 下載文件名稱(不包含擴展名)/// </summary>public string FileName { get; set; }/// <summary>/// 是否顯示標題/// </summary>public bool ShowTitle { get; set; }/// <summary>/// 標題/// </summary>public string Title { get; set; }/// <summary>/// ContentType/// </summary>public string ContentType { get; set; }/// <summary>/// 擴展名/// </summary>public string ExtName { get; set; }/// <summary>/// 獲取下載文件全名/// </summary>public string FullName { get { return FileName + ExtName; } }#endregion#region 構造函數public ExcelBaseResult(IList<T> entity, string fileName,bool showTitle,string title){this.Entity = entity;this.FileName = fileName;this.ShowTitle = showTitle;this.Title = title;}#endregion#region 抽象方法public abstract MemoryStream GetExcelStream();#endregion#region 重寫ExecuteResultpublic override void ExecuteResult(ControllerContext context){using(MemoryStream ms = GetExcelStream()){context.HttpContext.Response.AddHeader("Content-Length", ms.Length.ToString());context.HttpContext.Response.ContentType = ContentType;context.HttpContext.Response.AddHeader("Content-Disposition", "attachment; filename=" + FullName.EncodingDownloadFileName());ms.Seek(0, SeekOrigin.Begin);Stream output = context.HttpContext.Response.OutputStream;byte[] bytes = new byte[1024 * 10];int readSize = 0;while ((readSize = ms.Read(bytes, 0, bytes.Length)) > 0){output.Write(bytes, 0, readSize);context.HttpContext.Response.Flush();}}}#endregion} }主要因為Excel有不同版本,所有定義了一個父類,其子類只需要實現方法
public abstract MemoryStream GetExcelStream();
?
四、定義一個子類繼承ExcelBaseResult
這里實現了一個導出.xls格式(2003版本的)到子類
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Web; using System.Web.Mvc; using NPOI.HSSF.UserModel; using NPOI.SS.UserModel; using WebSeat.Entity.Member.Attributes; using WebSeat.Site.Member.CustomResult;namespace WebSeat.Site.Member.CustomResult {/// <summary>/// 說明:導出成.xls格式的Excel/// 創建日期:2016/12/13 13:51:23/// 創建人:曹永承/// </summary>public class Excel2003Result<T>: ExcelBaseResult<T> where T:new(){public Excel2003Result(IList<T> entity, string fileName,bool showTitle,string title):base(entity, fileName, showTitle, title){ContentType = "application/vnd.ms-excel";ExtName = ".xls";}public override MemoryStream GetExcelStream(){MemoryStream ms = new MemoryStream();//獲取實體屬性PropertyInfo[] propertys = typeof(T).GetProperties();if (propertys.Count() == 0){return ms;}//創建Excel對象IWorkbook book = new HSSFWorkbook();//添加一個sheetISheet sheet1 = book.CreateSheet("Sheet1");var index = ShowTitle ? 1 : 0;//樣式設置IFont cellfont = book.CreateFont();cellfont.FontHeightInPoints = 11;cellfont.FontName = "宋體";ICellStyle cellStyle = book.CreateCellStyle();cellStyle.VerticalAlignment = VerticalAlignment.Center;cellStyle.Alignment = HorizontalAlignment.Center;cellStyle.SetFont(cellfont);IRow rowColumnHead = sheet1.CreateRow(index);IDataFormat format = book.CreateDataFormat();ushort firstColumn = ushort.MaxValue, lastColumn = ushort.MinValue; //第一列下標和最后一列下標//添加列頭for (int j = 0; j < propertys.Count(); j++){ExcelDataOptionAttribute dataOption = propertys[j].GetCustomAttribute<ExcelDataOptionAttribute>();if (dataOption == null){continue;}IFont font = book.CreateFont();font.FontHeightInPoints = 11;font.FontName = "宋體";ICellStyle style = book.CreateCellStyle();style.VerticalAlignment = VerticalAlignment.Center;style.Alignment = HorizontalAlignment.Center;style.SetFont(font);if (!string.IsNullOrWhiteSpace(dataOption.Formater)){style.DataFormat = format.GetFormat(dataOption.Formater);}sheet1.SetDefaultColumnStyle(dataOption.ColumnIndex, style);ICell cell = rowColumnHead.CreateCell(dataOption.ColumnIndex);cell.SetCellValue(dataOption.DisplayName);firstColumn = firstColumn < dataOption.ColumnIndex ? firstColumn : dataOption.ColumnIndex;lastColumn = lastColumn > dataOption.ColumnIndex ? lastColumn : dataOption.ColumnIndex;}index = ShowTitle ? 2 : 1;//將各行數據顯示出來for (int i = 0; i < Entity.Count; i++){IRow row = sheet1.CreateRow(i + index);//循環各屬性,添加列for (int j = 0; j < propertys.Count(); j++){ExcelDataOptionAttribute dataOption = propertys[j].GetCustomAttribute<ExcelDataOptionAttribute>();if (dataOption == null){continue;}ICell cell = row.CreateCell(dataOption.ColumnIndex);//樣式設置//cell.CellStyle = cellStyle;if (dataOption.ColumnWidth != 0){sheet1.SetColumnWidth(dataOption.ColumnIndex, dataOption.ColumnWidth*256);}//根據數據類型判斷顯示格式if (propertys[j].PropertyType == typeof (int)){cell.SetCellValue((int)propertys[j].GetValue(Entity[i]));}else if (propertys[j].PropertyType == typeof (decimal) || propertys[j].PropertyType == typeof(double) || propertys[j].PropertyType == typeof(float)){cell.SetCellValue(Convert.ToDouble(propertys[j].GetValue(Entity[i])) );}else{cell.SetCellValue(propertys[j].GetValue(Entity[i]).ToString());}}}//將標題合并if (ShowTitle){IRow rowHead = sheet1.CreateRow(0);ICell cellHead = rowHead.CreateCell(firstColumn);cellHead.SetCellValue(Title);//樣式設置IFont font = book.CreateFont();font.FontHeightInPoints = 14;font.IsBold = true;ICellStyle style = book.CreateCellStyle();style.VerticalAlignment = VerticalAlignment.Center;style.Alignment = HorizontalAlignment.Center;style.SetFont(font);cellHead.CellStyle = style;rowHead.HeightInPoints = 20.25f;sheet1.AddMergedRegion(new NPOI.SS.Util.CellRangeAddress(0, 0, firstColumn, lastColumn));}book.Write(ms);ms.Seek(0, System.IO.SeekOrigin.Begin);return ms;}} }?
五、下載文件中文名稱出現亂碼問題
上面第二步,在Excel導出父類中,有這么一句代碼
context.HttpContext.Response.AddHeader("Content-Disposition", "attachment; filename=" + FullName.EncodingDownloadFileName()); 其中EncodingDownloadFileName方法是一個String的擴展類,用于將文件名稱進行編碼,避免出現亂碼的情況。
? ? ? ?之前測試過程中,在沒有使用轉碼的過程中,發現IE瀏覽器在下載時,中文名稱出現了亂碼的情況,其他瀏覽器正常(這里只測試了IE瀏覽器、谷歌瀏覽器、火狐瀏覽器、QQ瀏覽器、360瀏覽器和360極速瀏覽器)。后來使用了
?HttpContext.Current.Server.UrlEncode(filename)對文件名稱進行轉碼后發現,IE瀏覽器正常了,除了火狐瀏覽器,其他瀏覽器都正常。所有就想到當使用火狐瀏覽器訪問時不對名稱進行轉碼,后來寫一個String的擴展方法,方便后期其他下載類使用
using System; using System.Collections.Generic; using System.Linq; using System.Web;namespace WebSeat.Site.Member.Helper {/// <summary>/// 說明:String擴展方法/// 創建日期:2016/12/19 9:45:10/// 創建人:曹永承/// </summary>public static class StringHelperExtend{public static string EncodingDownloadFileName(this string filename){if (filename == null){throw new NullReferenceException("filename不能為空");}string agent = HttpContext.Current.Request.Headers["User-Agent"];//如果不是火狐瀏覽器都進行編碼if (agent != null && agent.ToLower().IndexOf("firefox") < 0){return HttpContext.Current.Server.UrlEncode(filename);}return filename;}} }?
六、代碼使用
public ActionResult ExportExcel(){//獲取數據IList<CityStatics> list = new List<CityStatics>();......ExcelBaseResult<CityStatics> excel = new Excel2003Result<CityStatics>(list, "1.xls", true, "全市各區數據 ? ?名稱:成都");return excel;}?
這樣就可以了,看看下載后的excel
?
補充說明:如果使用gzip壓縮方式的,那么文件下載時無法顯示文件大小的
總結
以上是生活随笔為你收集整理的C# MVC 自定义ActionResult实现EXCEL下载的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Excel 【小型成绩分析系统初稿】(功
- 下一篇: Unicode : RLO