前言

我们在做web系统中,导出也是很常用的一个功能,如果每一个数据列表都要对应写一个导出的方法不太现实。现在就想设计一个共通的功能来实现这个导出。

需求分析

在开始之前我们先要明白我们要实现怎样一个功能 

1、支持导出excel2003/excel2007 word pdf等文件格式 

2、支持数据分页,可以导出全部页或指定页 

3、支持导出的文档再压缩zip rar 7z 

4、支持导出多行的题头 

5、支持格式化,如将 0、1 转换为 男、女 

5、可拓展、可自定义等 

技术分析设计

1、生成文件基本可以使用开源的组件实现 

excel2003 使用 NPOI (有网友提醒我npoi已经支持2007了,我这里暂时还是只处理2003) 

excel2007 使用 EPPlus 

word        使用 DocX 还可以使用直接使用html table导出假的word 

pdf          使用 Gios Pdf 

其它格式预留接口…



2、B/S系统要分前后台,先看前台设计 

可以定义一个共通的js方法如com.exporter那么我们前台使用时应该要可以这样用:

com.exporter(grid)         把grid对象传递给前台导出对象,取得题头的相关信息及grid的url和queryParams传给后台重新取数据.paging(2,20)         分页处理表示每页20条,导出第2页,如果第两个参数为0则表示不分页.compress('zip’)      文件生成之后压缩成zip文件.download(‘xlsx’);    导出excel2007格式的文件

设计成这样基本足够,但是有可能我的grid中没有exporter需要的信息,不能直接com.exporter(grid)这种写法,或者大家不喜欢linq的这种写法,我们再设计另一种参数的写法:

com.exporter({fileType:’xls’,compressType:’none’,dataGetter:’api’,                   因为我们数据基本要到后台重新取数据,所以怎么取数据可能有很多种方法,dataGetter就是定义用哪一种方式取数据dataUrl:’/mms/api/recive’           接下来的dataUrl及dataParams都是dataGetter=‘api’中用到的参数dataParams:{BillNo:’20130701’,Supplier:’huawei’,page:1,rows:0},titles: [[]]                        多行的表头数据,跟easyui的多行表头格式设计一致
}).download();

那么前台基本就可以这样,有了这两种写法,使用起来就很方便了。



3、后台部分设计 

同样我们从调用的地方入手,在我的下载请求的方法中入手:

newExporter().Data(newApiData())                      定义数据取得方式.AddFormatter("sex",newSexFormatter())   添加sex列的formatter.Export(newPdfExport())                  设置导出pdf.Compress(newZipCompress())              压缩为zip.Download();                              下载文件向浏览器输出

我们要设计一个Exporter的类,它下面有Data Export Compress 等方法 

因为这些东西变动性很大,我取数据不能写死是怎么取的,所以我们定义一个取数据的接口来实现 

比如Exporter的Data方法

publicExporter Data(IDataGetter data)
{}

我在具体取数据的方法中继承IDataGetter接口并实现

public classApiData : IDataGetter
{public objectGetData(HttpContext context){....returndata;}
}

这样的话,假如我的取数据方式不太一样,我只要 new Exporter().Data(new XXDataGetter())就行了 

同样导出Export方法也一样,我定义了一个接口

public interfaceIExport{string suffix { get;}void MergeCell(int x1,int y1,int x2,inty2);void FillData(int x, int y,string field, objectdata);void Init(objectdata);Stream SaveAsStream();void SetHeadStyle(int x1, int y1, int x2, inty2);void SetRowsStyle(int x1, int y1, int x2, inty2);}

然后在 XlsExport XlsxExporter DocExporter PdfExporter中实现这个接口就可,然后就可以实现导出不同格式的文档的 

压缩方法Comress及字段格式化方法AddFormatter都同样定义接口,同样有人也不喜欢Exporter的写法,那我们再定义一种和前台差不的写法

newExporter().Data("api").Export("xlsx").Compress("zip").Download();

这样的话后台这个exporter类也挺好用的了,那么接下来我们再来谈谈如何具体实现吧

技术实现

设计时我们先实现后台 

一、后台其实我已经实现出来了,截个图给大家看看先 



1、首先我们定义一些基本的元素 

定义题头结构

public classColumn{publicColumn(){rowspan= 1;colspan= 1;}public string field { get; set; }public string title { get; set; }public int rowspan { get; set; }public int colspan { get; set; }public bool hidden { get; set; }}

定义压缩接口

usingSystem;usingSystem.Collections.Generic;usingSystem.IO;usingSystem.Linq;usingSystem.Text;namespaceZephyr.Core
{public interfaceICompress{string Suffix(stringorgSuffix);Stream Compress(Stream fileStream,string fullName);}
}

定义数据获取的接口

usingSystem.Web;namespaceZephyr.Core
{public interfaceIDataGetter{objectGetData(HttpContext context);}
}

定义文件导出的接口

usingSystem.IO;namespaceZephyr.Core
{public interfaceIExport{string suffix { get;}void MergeCell(int x1,int y1,int x2,inty2);void FillData(int x, int y,string field, objectdata);void Init(objectdata);Stream SaveAsStream();void SetHeadStyle(int x1, int y1, int x2, inty2);void SetRowsStyle(int x1, int y1, int x2, inty2);}
}

定义格式化接口

namespaceZephyr.Core
{public interfaceIFormatter{object Format(objectvalue);}
}

2、基本的元素都定义好了,我们再来实现Exporter导出类,关键性的代码就在这里了

usingSystem;usingSystem.Collections.Generic;usingSystem.IO;usingSystem.Web;usingNewtonsoft.Json;usingZephyr.Utils;namespaceZephyr.Core
{public classExporter{const string DEFAULT_EXPORT = "xls";const string DEFAULT_DATAGETTER = "api";const string DEFAULT_COMPRESS = "none";private Dictionary<string, Type> _compress = new Dictionary<string, Type>() { {"zip", typeof(ZipCompress)},{"none",typeof(NoneCompress)}};private Dictionary<string, Type> _dataGetter = new Dictionary<string, Type>() { {"api", typeof(ApiData) } };private Dictionary<string, Type> _export = new Dictionary<string, Type>() { {"xls", typeof(XlsExport) }, {"xlsx", typeof(XlsxExport) } ,{"doc", typeof(HtmlDocExport) },{"pdf", typeof(PdfExport) }};private Dictionary<string,IFormatter> _fieldFormatter = new Dictionary<string,IFormatter>();private object_data;private List<List<Column>>_title;private Stream _fileStream = null;private string _fileName = string.Empty;private string _suffix = string.Empty;public staticExporter Instance(){var export = newExporter();var context =HttpContext.Current;if (context.Request.Form["titles"]!=null)export.Title(JsonConvert.DeserializeObject<List<List<Column>>>(context.Request.Form["titles"]));if (context.Request.Form["dataGetter"] != null)export.Data(context.Request.Form["dataGetter"]);if (context.Request.Form["fileType"] != null)export.Export(context.Request.Form["fileType"]);if (context.Request.Form["compressType"] != null)export.Compress(context.Request.Form["compressType"]);returnexport;}publicExporter Data(IDataGetter data){_data=data.GetData(HttpContext.Current);return this;}public Exporter Data(stringtype){var dataGetter = GetActor<IDataGetter>(_dataGetter, DEFAULT_DATAGETTER,type);returnData(dataGetter);}public Exporter Data(objectdata){_data=data;return this;}public Exporter AddFormatter(stringfield,IFormatter formatter){_fieldFormatter[field]=formatter;return this;}public Exporter Title(List<List<Column>>title){_title=title;return this;}public Exporter FileName(stringfileName){_fileName=fileName;return this;}public Exporter Export(stringtype){var export = GetActor<IExport>(_export, DEFAULT_EXPORT, type);returnExport(export);}publicExporter Export(IExport export){if (_title == null){_title= new List<List<Column>>();_title.Add(new List<Column>());EachHelper.EachListHeader(_data, (i, field, type)=> _title[0].Add(new Column() { title = field, field = field, rowspan = 1, colspan = 1}));}Dictionary<int, int> currentHeadRow = new Dictionary<int, int>();Dictionary<string, List<int>> fieldIndex = new Dictionary<string, List<int>>();Func<int, int> GetCurrentHeadRow = cell => currentHeadRow.ContainsKey(cell) ? currentHeadRow[cell] : 0;var currentRow = 0;var currentCell = 0;export.Init(_data);//生成多行题头for (var i = 0; i < _title.Count; i++){currentCell= 0;for (var j = 0; j < _title[i].Count; j++){var item =_title[i][j];if (item.hidden) continue;while (currentRow <GetCurrentHeadRow(currentCell)) currentCell++;export.FillData(currentCell, currentRow,"title_" +item.field, item.title);if (item.rowspan + item.colspan > 2)export.MergeCell(currentCell, currentRow, currentCell+ item.colspan - 1, currentRow + item.rowspan - 1);if (!string.IsNullOrEmpty(item.field)){if (!fieldIndex.ContainsKey(item.field))fieldIndex[item.field]= new List<int>();fieldIndex[item.field].Add(currentCell);}for (var k = 0; k < item.colspan; k++)currentHeadRow[currentCell]= GetCurrentHeadRow(currentCell++) +item.rowspan;}currentRow++;}//设置题头样式export.SetHeadStyle(0, 0, currentCell - 1, currentRow - 1);//设置数据样式var dataCount = 0;EachHelper.EachListRow(_data, (i, r)=> dataCount++);export.SetRowsStyle(0, currentRow, currentCell - 1, currentRow + dataCount - 1);//填充数据EachHelper.EachListRow(_data, (rowIndex, rowData) =>{EachHelper.EachObjectProperty(rowData, (i, name, value)=>{if(fieldIndex.ContainsKey(name))foreach (int cellIndex infieldIndex[name]){if(_fieldFormatter.ContainsKey(name)) value=_fieldFormatter[name](value);export.FillData(cellIndex, currentRow, name, value);}});currentRow++;});_fileStream=export.SaveAsStream();_suffix=export.suffix;if (string.IsNullOrEmpty(_fileName))_fileName= DateTime.Now.ToString("yyyyMMddHHmmss");return this;}public Exporter Compress(stringtype){var compress = GetActor<ICompress>(_compress, DEFAULT_COMPRESS, type);returnCompress(compress);}publicExporter Compress(ICompress compress){_fileStream= compress.Compress(_fileStream,string.Format("{0}.{1}",_fileName,_suffix));_suffix=compress.Suffix(_suffix);return this;}public voidDownload(){if (_fileStream != null && _fileStream.Length > 0)ZFiles.DownloadFile(HttpContext.Current, _fileStream,string.Format("{0}.{1}",_fileName,_suffix), 1024 * 1024 * 10);}private T GetActor<T>(Dictionary<string, Type> dict, string defaultKey, stringkey){if (!dict.ContainsKey(key))key=defaultKey;return(T)Activator.CreateInstance(dict[key]);}}
}

3、Exporter类实现之后我们还要实现一些接口才能使用 

我们这里就先实现 

IDataGetter   ApiData 

IExport         XlsxExport XlsExport HtmlDocExport PdfExport 

IComresss     ZipCompress 

其它的以后再实现就可以了,很方便 



ApiData

usingSystem;usingSystem.Dynamic;usingSystem.Linq;usingSystem.Web;usingNewtonsoft.Json;namespaceZephyr.Core
{public classApiData : IDataGetter{public objectGetData(HttpContext context){dynamic data= null;var url = context.Request.Form["dataAction"];var param = JsonConvert.DeserializeObject<dynamic>(context.Request.Form["dataParams"]);var route = url.Replace("/api/", "").Split('/'); //route[0]=mms,route[1]=send,route[2]=getvar type = Type.GetType(String.Format("Zephyr.Areas.{0}.Controllers.{1}ApiController,Zephyr.Web", route), false, true);if (type != null){var instance =Activator.CreateInstance(type);var action = route.Length > 2 ? route[2] : "Get";var methodInfo =type.GetMethod(action);var parameters = new object[] { newRequestWrapper().SetRequestData(param) };data=methodInfo.Invoke(instance, parameters);if (data.GetType() == typeof(ExpandoObject)){if ((data as ExpandoObject).Where(x => x.Key == "rows").Count() > 0)data=data.rows;}}returndata;}}
}



XlsxExport 导出excel2007 利用 Epplus实现

usingSystem;usingSystem.Collections.Generic;usingSystem.IO;usingSystem.Linq;usingSystem.Text;usingSystem.Drawing;usingZephyr.Utils;usingZephyr.Utils.EPPlus;usingZephyr.Utils.EPPlus.Style;namespaceZephyr.Core
{public classXlsxExport:IExport{public string suffix { get {return "xlsx"; } }privateExcelPackage package;privateExcelWorksheet sheet;public void Init(objectdata){package= newExcelPackage();sheet= package.Workbook.Worksheets.Add("sheet1");}public void MergeCell(int x1,int y1,int x2,inty2){sheet.Cells[y1+1, x1+1, y2+1, x2+1].Merge = true;}public virtual void FillData(int x, int y,string field, objectvalue){if (ZGeneric.IsTypeIgoreNullable<DateTime>(value))sheet.Cells[y+ 1, x + 1].Style.Numberformat.Format = "yyyy-MM-dd hh:mm:ss";sheet.Cells[y+ 1, x + 1].Value =value;}public virtual void SetHeadStyle(int x1, int y1, int x2, inty2){using (var head = sheet.Cells[y1 + 1, x1 + 1, y2 + 1, x2 + 1]) //set head style
{head.Style.Font.Bold= true;head.Style.Font.Size= 12;head.Style.Font.Name= "Arial";head.Style.Border.Top.Style=ExcelBorderStyle.Thin;head.Style.Border.Top.Color.SetColor(Color.Gray);head.Style.Border.Right.Style=ExcelBorderStyle.Thin;head.Style.Border.Right.Color.SetColor(Color.Gray);head.Style.Border.Bottom.Style=ExcelBorderStyle.Thin;head.Style.Border.Bottom.Color.SetColor(Color.Gray);head.Style.Border.Left.Style=ExcelBorderStyle.Thin;head.Style.Border.Left.Color.SetColor(Color.Gray);head.Style.VerticalAlignment=ExcelVerticalAlignment.Center;head.Style.HorizontalAlignment=ExcelHorizontalAlignment.Center;head.Style.Fill.PatternType=ExcelFillStyle.Solid;head.Style.Fill.BackgroundColor.SetColor(Color.LightBlue);}}public virtual void SetRowsStyle(int x1, int y1, int x2, inty2){using (var data = sheet.Cells[y1 + 1, x1 + 1, y2 + 1, x2 + 1])//set data style
{data.Style.Font.Name= "Arial";data.Style.Font.Size= 11;data.Style.Border.Top.Style=ExcelBorderStyle.Thin;data.Style.Border.Top.Color.SetColor(Color.Gray);data.Style.Border.Right.Style=ExcelBorderStyle.Thin;data.Style.Border.Right.Color.SetColor(Color.Gray);data.Style.Border.Bottom.Style=ExcelBorderStyle.Thin;data.Style.Border.Bottom.Color.SetColor(Color.Gray);data.Style.Border.Left.Style=ExcelBorderStyle.Thin;data.Style.Border.Left.Color.SetColor(Color.Gray);}}publicStream SaveAsStream(){var ms = newMemoryStream();package.SaveAs(ms);package= null;sheet= null;returnms;}}
}



XlsExport 导出excel2003 利用NPOI实现

usingSystem.IO;usingZephyr.Utils;usingZephyr.Utils.NPOI.HSSF.UserModel;usingZephyr.Utils.NPOI.SS.UserModel;usingZephyr.Utils.NPOI.HSSF.Util;usingZephyr.Utils.NPOI.SS.Util;namespaceZephyr.Core
{public classXlsExport:IExport{public string suffix { get {return "xls"; } }privateHSSFWorkbook workbook;privateISheet sheet;public void Init(objectdata){workbook= newHSSFWorkbook();sheet= workbook.CreateSheet("sheet1");sheet.DefaultRowHeight= 200 * 20;}public void MergeCell(int x1,int y1,int x2,inty2){CellRangeAddress range= newCellRangeAddress(y1, y2, x1, x2);sheet.AddMergedRegion(range);  }public virtual void FillData(int x, int y,string field, objectvalue){var row = sheet.GetRow(y) ??sheet.CreateRow(y);var cell = row.GetCell(x) ??row.CreateCell(x);switch ((value ?? string.Empty).GetType().Name.ToLower()){case "int32":case "int64":case "decimal":cell.CellStyle.Alignment=HorizontalAlignment.RIGHT;cell.SetCellValue(ZConvert.To<double>(value, 0));break;default:cell.SetCellValue(ZConvert.ToString(value));break;}}public virtual void SetHeadStyle(int x1, int y1, int x2, inty2){var style =GetHeadStyle();for (var y = y1; y <= y2; y++){var row = sheet.GetRow(y) ??sheet.CreateRow(y);for (var x = x1; x <= x2; x++){var cell = row.GetCell(x) ??row.CreateCell(x);cell.CellStyle=style;}}}public virtual void SetRowsStyle(int x1, int y1, int x2, inty2){var style =GetDataStyle();for (var y = y1; y <= y2; y++){var row = sheet.GetRow(y) ??sheet.CreateRow(y);for (var x = x1; x <= x2; x++){var cell = row.GetCell(x) ??row.CreateCell(x);cell.CellStyle=style;}}}publicStream SaveAsStream(){var ms = newMemoryStream();workbook.Write(ms);ms.Flush();ms.Position= 0;workbook= null;sheet= null;returnms;}privateICellStyle GetHeadStyle(){//表头样式var headStyle =workbook.CreateCellStyle();headStyle.Alignment= HorizontalAlignment.CENTER;//居中对齐headStyle.VerticalAlignment =VerticalAlignment.CENTER;//表头单元格背景色headStyle.FillForegroundColor =HSSFColor.LIGHT_GREEN.index;headStyle.FillPattern=FillPatternType.SOLID_FOREGROUND;//表头单元格边框headStyle.BorderTop =BorderStyle.THIN;headStyle.TopBorderColor=HSSFColor.BLACK.index;headStyle.BorderRight=BorderStyle.THIN;headStyle.RightBorderColor=HSSFColor.BLACK.index;headStyle.BorderBottom=BorderStyle.THIN;headStyle.BottomBorderColor=HSSFColor.BLACK.index;headStyle.BorderLeft=BorderStyle.THIN;headStyle.LeftBorderColor=HSSFColor.BLACK.index;//表头字体设置var font =workbook.CreateFont();font.FontHeightInPoints= 12;//字号font.Boldweight = 600;//加粗//font.Color = HSSFColor.WHITE.index;//颜色
headStyle.SetFont(font);returnheadStyle;}privateICellStyle GetDataStyle(){//数据样式var dataStyle =workbook.CreateCellStyle();dataStyle.Alignment= HorizontalAlignment.LEFT;//左对齐//数据单元格的边框dataStyle.BorderTop =BorderStyle.THIN;dataStyle.TopBorderColor=HSSFColor.BLACK.index;dataStyle.BorderRight=BorderStyle.THIN;dataStyle.RightBorderColor=HSSFColor.BLACK.index;dataStyle.BorderBottom=BorderStyle.THIN;dataStyle.BottomBorderColor=HSSFColor.BLACK.index;dataStyle.BorderLeft=BorderStyle.THIN;dataStyle.LeftBorderColor=HSSFColor.BLACK.index;//数据的字体var datafont =workbook.CreateFont();datafont.FontHeightInPoints= 11;//字号
dataStyle.SetFont(datafont);returndataStyle;}}
}



PdfExport 利用Gios pdf实现

usingSystem;usingSystem.Collections.Generic;usingSystem.IO;usingSystem.Drawing;usingZephyr.Utils;usingZephyr.Utils.Gios.Pdf;usingSystem.Data;namespaceZephyr.Core
{public classPdfExport:IExport{public string suffix { get {return "pdf"; } }privateDataTable table;private List<string>title;public void Init(objectdata){var type =ZGeneric.GetGenericType(data);var tableName = ZGeneric.IsDynamicType(type) ? string.Empty : type.Name;table= newDataTable(tableName);EachHelper.EachListHeader(data, (rowIndex, name, cellType)=>{string typeName =cellType.ToString();if(cellType.IsGenericType)typeName= cellType.GetGenericArguments()[0].ToString();Type newType= Type.GetType(typeName, false);if (newType != null)table.Columns.Add(name, newType);});table.BeginLoadData();title= new List<string>();}public void MergeCell(int x1,int y1,int x2,inty2){throw new Exception("pdf未实现多选title");}public virtual void FillData(int x, int y,string field, objectvalue){if (field.StartsWith("title_")){title.Add(field.Split('_')[1]);return;}if (table.Rows.Count<y)table.Rows.Add(table.NewRow());if (value != null && (Type.GetType(value.GetType().ToString(), false) != null))table.Rows[y-1][field] =value;}public virtual void SetHeadStyle(int x1, int y1, int x2, inty2){}public virtual void SetRowsStyle(int x1, int y1, int x2, inty2){}publicStream SaveAsStream(){table.EndLoadData();table.AcceptChanges();var removes = new List<string>();foreach (DataColumn dc intable.Columns)if (title.IndexOf(dc.ColumnName) == -1)                    
removes.Add(dc.ColumnName);
foreach(var name inremoves)
table.Columns.Remove(name);
var pdfTitle =table.TableName;//Starting instantiate the document.//Remember to set the Docuement Format. In this case, we specify width and height.PdfDocument myPdfDocument = new PdfDocument(PdfDocumentFormat.InCentimeters(21, 29.7));//Now we create a Table of 100 lines, 6 columns and 4 points of Padding.PdfTable myPdfTable = myPdfDocument.NewTable(new Font("Arial", 12), table.Rows.Count, table.Columns.Count, 4);//Importing datas from the datatables... (also column names for the headers!)//myPdfTable.ImportDataTable(Table);
myPdfTable.ImportDataTable(table);//Sets the format for correct date-time representation//myPdfTable.Columns[2].SetContentFormat("{0:dd/MM/yyyy}");//Now we set our Graphic Design: Colors and Borders...
myPdfTable.HeadersRow.SetColors(Color.White, Color.Navy);myPdfTable.SetColors(Color.Black, Color.White, Color.Gainsboro);myPdfTable.SetBorders(Color.Black,1, BorderType.CompleteGrid);//// With just one method we can set the proportional width of the columns.//// It's a "percentage like" assignment, but the sum can be different from 100.//myPdfTable.SetColumnsWidth(new int[] { 5, 25, 16, 20, 20, 15 });//// You can also set colors for a range of cells, in this case, a row://myPdfTable.Rows[7].SetColors(Color.Black, Color.LightGreen);//Now we set some alignment... for the whole table and then, for a column.
myPdfTable.SetContentAlignment(ContentAlignment.MiddleCenter);myPdfTable.Columns[1].SetContentAlignment(ContentAlignment.MiddleLeft);//Here we start the loop to generate the table...while (!myPdfTable.AllTablePagesCreated){//we create a new page to put the generation of the new TablePage:PdfPage newPdfPage =myPdfDocument.NewPage();PdfTablePage newPdfTablePage= myPdfTable.CreateTablePage(new PdfArea(myPdfDocument, 48, 120, 500, 670));//we also put a LabelPdfTextArea pta = new PdfTextArea(new Font("Arial", 26, FontStyle.Bold), Color.Red,new PdfArea(myPdfDocument, 0, 20, 595, 120), ContentAlignment.MiddleCenter, pdfTitle);//nice thing: we can put all the objects in the following lines, so we can have//a great control of layer sequence...
newPdfPage.Add(newPdfTablePage);newPdfPage.Add(pta);//we save each generated page before start rendering the next.
newPdfPage.SaveToDocument();}//myPdfDocument.SaveToFile("Example1.pdf");var stream = newMemoryStream();myPdfDocument.SaveToStream(stream);returnstream;}}
}



HtmlDocExport 导出word 这个是直接导出html table,不是真正的word,如果要用真正的word要利用DocX来实现,我还没来的及写,以后再贴出来吧

usingSystem;usingSystem.Collections.Generic;usingSystem.IO;usingSystem.Linq;usingSystem.Text;namespaceZephyr.Core
{public classHtmlDocExport:IExport{public string suffix { get {return "doc"; } }privateStringBuilder sBuilder;private introwIndex;private Dictionary<int,object>row;public void Init(objectdata){rowIndex= 0;row= new Dictionary<int, object>();sBuilder= newStringBuilder();sBuilder.Append("<table cellspacing=\"0\" rules=\"all\" border=\"1\" style=\"border-collapse:collapse;\">");sBuilder.Append("<tr>");}public void MergeCell(int x1,int y1,int x2,inty2){throw new Exception("htmldoc未实现多选title");}public virtual void FillData(int x, int y,string field, objectvalue){if (rowIndex <y){AppendRow(row.OrderBy(m=> m.Key).Select(m =>m.Value).ToArray());row= new Dictionary<int, object>();rowIndex++;}row[x]=value;}public virtual void SetHeadStyle(int x1, int y1, int x2, inty2){}public virtual void SetRowsStyle(int x1, int y1, int x2, inty2){}publicStream SaveAsStream(){AppendRow(row.OrderBy(m=> m.Key).Select(m =>m.Value).ToArray());sBuilder.Append("</table");byte[] byteArray =Encoding.Default.GetBytes(sBuilder.ToString());var stream = newMemoryStream(byteArray);returnstream;}private void AppendRow(object[] values){sBuilder.Append("<tr>");foreach(var value invalues)sBuilder.Append(string.Format("<td>{0}</td>", value??string.Empty ));sBuilder.Append("</tr>");}}
}

ZipCompress利用Ionic.Zip实现压缩

usingSystem;usingSystem.Collections.Generic;usingSystem.IO;usingSystem.Linq;usingSystem.Text;usingZephyr.Utils.Ionic.Zip;namespaceZephyr.Core
{public classZipCompress: ICompress{public string Suffix(stringorgSuffix){return "zip";}publicStream Compress(Stream fileStream,string fullName){using (var zip = newZipFile()){zip.AddEntry(fullName, fileStream);Stream zipStream= newMemoryStream();zip.Save(zipStream);returnzipStream;}}}
}

Formatter实现示例

usingSystem;namespaceZephyr.Core
{public classSexFormatter:IFormatter{public object Format(objectvalue){switch(Convert.ToString(value)){case "0":return "纯爷们";case "1":return "女汉子";default:return "春哥";}}}
}

实现了以上这样接口,那么这个Exporter类的功能基本就OK了,如果要拓展一些其它格式以及自定义一些只要实现这些接口就可以,使用很方便了。 

那么我们就应用吧,MVC 及webform都可以,我使用的是mvc,在HomeController下添加一个Download的Action,只要添加一句代码就可以实现了

public voidDownload()
{Exporter.Instance().Download();
}

除了在设计中说的到那些功能,这个Instance方法要再说一下,可以参照Exporter中的代码看,Instance中我有去请求中取参数titles dataGetter fileType compressType等参数并且实现。所以下载action中只需要这么简单的一句代码就搞定了。我们继续看前台吧

二、前台实现 

前台只有一段共通的脚本,很简单

com.exporter =function (opt) {var self = this;var defaultOptions ={action:"/home/download",dataGetter:"api",dataAction:"",dataParams: {},titles: [[]],fileType:'xls',compressType:'none'};this.paging =function (page,rows) {self.params.dataParams.page =page;self.params.dataParams.rows =rows;returnself;};this.compress =function () {self.params.compressType = 'zip';returnself;};this.title =function (filed,title) {self.params.titles[0][filed] =title;returnself;};this.download =function (suffix) {self.params.fileType = suffix || "xls";self.params.dataParams = JSON.stringify(self.params.dataParams);self.params.titles = JSON.stringify(self.params.titles);
        var downloadHelper = $('<iframe style="display:none;" id="downloadHelper"></iframe>').appendTo('body')[0];var doc =downloadHelper.contentWindow.document;if(doc) {doc.open();doc.write('')//微软为doc.clear()有时会出bugdoc.writeln(utils.formatString("<html><body><form id='downloadForm' name='downloadForm' method='post' action='{0}'>"
, self.params.action));for (var key in self.params) 
                doc.writeln(utils.formatString("<input type='hidden' name='{0}' value='{1}'>", key, self.params[key]));doc.writeln('<\/form><\/body><\/html>');doc.close();var form = doc.forms[0];if(form) {form.submit();}}};initFromGrid=function (grid) {var options = grid.$element().datagrid('options');if(grid.treegrid)options.url= options.url || grid.treegrid('options').url;var titles = [[]], length =Math.max(options.frozenColumns.length, options.columns.length);for (var i = 0; i < length; i++)titles[i]= (options.frozenColumns[i] || []).concat(options.columns[i] ||[])self.params = $.extend(true, {}, defaultOptions, {dataAction: options.url,dataParams: options.queryParams,titles: titles});};if (opt.$element)initFromGrid(opt);elseself.params = $.extend(true, {}, defaultOptions, opt);returnself;
};

大家基本都能看懂,不需要我再解释了。下载的原理是动态创建了一个iframe把参数写到input中提交到后台。



功能测试



测试 html代码

<divid="dropdown"style="width:100px; display:none;">  <divdata-options="iconCls:'icon-ext-xls'"data-bind="click:downloadClick1">Excel2003表格所有页</div>  <divdata-options="iconCls:'icon-ext-xls'"data-bind="click:downloadClick2">Excel2003表格指定页</div> <divdata-options="iconCls:'icon-ext-rar'"data-bind="click:downloadClick3">Excel2003压缩zip</div>  <divdata-options="iconCls:'icon-page_excel'"data-bind="click:downloadClick4">Excel2007/2010</div>  <divdata-options="iconCls:'icon-ext-pdf'"data-bind="click:downloadClick5">PDF</div>  <divdata-options="iconCls:'icon-ext-doc'"data-bind="click:downloadClick6">Word</div>
</div>

测试 js代码

this.downloadClick1 =function(){com.exporter(self.grid).download("xls");};this.downloadClick2 =function(){com.exporter(self.grid).paging(2,3).download("xls"); 只做了5条数据,就3条一页吧,导出第2页};this.downloadClick3 =function(){com.exporter(self.grid).compress('zip').download("xls");};this.downloadClick4 =function(){com.exporter(self.grid).download("xlsx");};this.downloadClick5 =function(){com.exporter(self.grid).download("pdf");};this.downloadClick6 =function(){com.exporter(self.grid).download("doc");};

测试结果 

1 Excel2003表格所有页 com.exporter(self.grid).download("xls"); 

2 Excel2003表格指定页  com.exporter(self.grid).page(2,3).download("xls"); 

3 Excel2003压缩zip com.exporter(self.grid).compress('zip').download("xls"); 

4 Excel2007/2010  com.exporter(self.grid).download("xlsx"); 

5 PDF导出 com.exporter(self.grid).download("pdf"); Gios这个组件出现中文会乱码,我想了很多办法都解决不了,没办法,可能要找一另一个pdf组件,先隐藏中文的字段截个图给大家看吧 

6 Word导出 com.exporter(self.grid).download("doc");

测试导出多行题头,比如这个页面是多行的 

1 导出excel2003 多行题头 

2 导出excel2007 多行题头 

还有一些其它的功能我就不一一测试了,大家有兴趣可以自己再研究

在我们的材料管理的系统中导出一般只用几个选项,1 导出全部页excel2003 2 导出全部页excel2007/2010 3导出全部页word 这三个 

所代码也很简单,代码如下:

<divid="dropdown"style="width:100px; display:none;">  <divdata-options="iconCls:'icon-ext-xls'"suffix="xls"data-bind="click:downloadClick">Excel2003   </div>  <divdata-options="iconCls:'icon-page_excel'"suffix="xlsx"data-bind="click:downloadClick">Excel2007   </div>  <divdata-options="iconCls:'icon-ext-doc'"suffix="doc"data-bind="click:downloadClick">Word2003    </div>
</div>
this.downloadClick = function (vm, event) {com.exporter(self.grid).download($(event.currentTarget).attr("suffix"));
};

在我之前的博客中写共通的查询viewModel中有提到导出功能,但都只有上面的一句代码。估计大家看了这篇博客才能明白。



后述

已经把这个材料管理系统挂在我们的网站上了。(demo地址被认为是广告,被删除了)

我有好长一段时间没写博客了。今天正好有点时间就把我们框架中的导出功能跟大家分享一下。 

如果大家喜欢就帮[推荐]一下吧,大家的支持才是我写博客的动力。

转载自:http://www.cnblogs.com/xqin/p/3165258.html

通用的web系统数据导出功能设计实现(导出excel2003/2007 word pdf zip等)相关推荐

  1. 大型web系统数据缓存设计-l转载

    原文地址:http://www.wmyouxi.com/a/60368.html#ixzz3tGYG9JwC 1. 前言 在高访问量的web系统中,缓存几乎是离不开的:但是一个适当.高效的缓存方案设计 ...

  2. 大型web系统数据缓存设计

    [IT168 技术]在高访问量的web系统中,缓存几乎是离不开的;但是一个适当.高效的缓存方案设计却并不容易;所以接下来将讨论一下应用系统缓存的设计方面应该注意哪些东西,包括缓存的选型.常见缓存系统的 ...

  3. Win10系统中不需要打开即可预览word/pdf

    今天看MOOC视频的时候学到一招win10中快速预览word/pdf提高效率的方法:选中目标文件,查看,浏览窗格即可.简单高效,MARK!

  4. (转)基于MVC4+EasyUI的Web开发框架经验总结(10)--在Web界面上实现数据的导入和导出...

    http://www.cnblogs.com/wuhuacong/p/3873498.html 数据的导入导出,在很多系统里面都比较常见,这个导入导出的操作,在Winform里面比较容易实现,我曾经在 ...

  5. web系统中的结构化数据标记

    Web 系统的设计要点之一是内容和表示的分离,网站以HTML发布内容,对内容进行操作的服务也只能访问 HTML.随着表现形式各异的设备在大量地增加,也大大增加了网站针对不同表示格式的数量.同时,一些新 ...

  6. boll指标 java_股票数据计算分析 web 系统 stock-job-web v1.0 发布了

    stock-job-web V1 是基于Python的pandas,tushare,bokeh,tornado,stockstats,ta-lib等框架开发的全栈股票系统. 1)可以直接使用docke ...

  7. 久其通用数据管理平台_银保行业通用的CRM系统,为你轻松化解庞大数据难题

    内容简介 本文以中国人寿财产保险股份有限公司某市的分公司为例,介绍智慧平台通过为该公司量身定制银保业通用的CRM系统,让各层级的管理者通过手机/电脑端便能在线提报相关数据,使数据收集.数据管理.流程分 ...

  8. Axure高保真智慧消防远程监管系统数据可视化大屏看板+web端高保真大数据分析平台看板+大数据交换配置管理平台大屏动态可视化看板

    作品介绍:Axure高保真智慧消防远程监管系统数据可视化大屏看板+web端高保真大数据分析平台看板+大数据交换配置管理平台大屏动态可视化看板 原型交互及下载链接:https://www.pmdaniu ...

  9. java导出excel 序号_java web将数据导出为Excel格式文件代码片段

    本文实例为大家分享了java web将数据导出为Excel格式文件的具体代码,供大家参考,具体内容如下 1.jsp代码 2.js代码 function getVerExcel() { window.l ...

最新文章

  1. 【iOS】sqlite3的使用(増删改查)
  2. 【分块答案】【最小割】bzoj1532 [POI2005]Kos-Dicing
  3. 一次实现可以在某些场合替代菱形继承?
  4. python嵌套循环跳出_如何跳出嵌套的while循环
  5. 重置studio 3T 14天试用
  6. python 运行时 变量_python运行过程,变量,符号
  7. python中unexpectedtoken怎么解决_linux后台执行./run.py提示python syntax error near unexpected token `('...
  8. Android底部导航栏的实现(RadioGroup和Fragment结合使用)
  9. oracle练习之查询1(where等)
  10. java 各省市区代码表
  11. GDI 总结三: CImage类使用
  12. 《Fundamentals of Computer Grahpics》虎书第三版翻译——第一章 介绍
  13. 【零信任落地案例】吉大正元某大型集团公司零信任实践案例
  14. python 将列表中的英文或者拼音转换为中文
  15. 【java】java编写 if 时不带 else
  16. 一文彻底说明分布式事务原理
  17. 软考 | 2009年下半年 软件设计师 下午试卷
  18. 微软官方最新的免费VB电子书下载
  19. PostgreSQL数据库实战培训课程(2天速成版)
  20. 详解9个写进简历的数据分析项目

热门文章

  1. 我的毕业四年总结及对未来的期许!
  2. RAKsmart CN2 VPS主机性能评测
  3. 苹果、google、微软的那些事(iPhone)
  4. Linux上配置BIP语言编译器及引擎
  5. AssertionError: train: No labels found in ****\train.cache报错
  6. 语音特征信号分类---BP神经网络---MATLAB实现
  7. 高考数学有得用计算机吗,高中数学为什么不让用计算器?
  8. 综合案例——手写数字图像处理算法比较
  9. js 排班插件_js jquery 实现 排班,轮班,日历,日程。使用fullcalendar 插件
  10. 复旦大学,计算机考研情况