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

时间:2023-03-09 15:27:29
通用的web系统数据导出功能设计实现(导出excel2003/2007 word pdf zip等)

前言

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

需求分析

在开始之前我们先要明白我们要实现怎样一个功能
1、支持导出excel2003/excel2007 word pdf等文件格式
2、支持数据分页,可以导出全部页或指定页
3、支持导出的文档再压缩zip rar 7z
4、支持导出多行的题头
5、支持格式化,如将 0、1 转换为 男、女
5、可拓展、可自定义等
通用的web系统数据导出功能设计实现(导出excel2003/2007 word pdf zip等)

技术分析设计

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、后台部分设计

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

    new Exporter()
.Data(new ApiData()) 定义数据取得方式
.AddFormatter("sex",new SexFormatter()) 添加sex列的formatter
.Export(new PdfExport()) 设置导出pdf
.Compress(new ZipCompress()) 压缩为zip
.Download(); 下载文件向浏览器输出

我们要设计一个Exporter的类,它下面有Data Export Compress 等方法
因为这些东西变动性很大,我取数据不能写死是怎么取的,所以我们定义一个取数据的接口来实现

比如Exporter的Data方法

public Exporter Data(IDataGetter data)
{ }

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

public class ApiData : IDataGetter
{
public object GetData(HttpContext context)
{
....
return data;
}
}

这样的话,假如我的取数据方式不太一样,我只要 new Exporter().Data(new XXDataGetter())就行了
同样导出Export方法也一样,我定义了一个接口

    public interface IExport
{
string suffix { get;} void MergeCell(int x1,int y1,int x2,int y2);
void FillData(int x, int y,string field, object data); void Init(object data);
Stream SaveAsStream(); void SetHeadStyle(int x1, int y1, int x2, int y2);
void SetRowsStyle(int x1, int y1, int x2, int y2);
}

然后在 XlsExport XlsxExporter DocExporter PdfExporter中实现这个接口就可,然后就可以实现导出不同格式的文档的
压缩方法Comress及字段格式化方法AddFormatter都同样定义接口,同样有人也不喜欢Exporter的写法,那我们再定义一种和前台差不的写法

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

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

技术实现

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

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

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

定义题头结构

    public class Column
{
public Column()
{
rowspan = ;
colspan = ;
}
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; }
}

定义压缩接口

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text; namespace Zephyr.Core
{
public interface ICompress
{
string Suffix(string orgSuffix);
Stream Compress(Stream fileStream,string fullName);
}
}

定义数据获取的接口

using System.Web;

namespace Zephyr.Core
{
public interface IDataGetter
{
object GetData(HttpContext context);
}
}

定义文件导出的接口

using System.IO;

namespace Zephyr.Core
{
public interface IExport
{
string suffix { get;} void MergeCell(int x1,int y1,int x2,int y2);
void FillData(int x, int y,string field, object data); void Init(object data);
Stream SaveAsStream(); void SetHeadStyle(int x1, int y1, int x2, int y2);
void SetRowsStyle(int x1, int y1, int x2, int y2);
}
}

定义格式化接口

namespace Zephyr.Core
{
public interface IFormatter
{
object Format(object value);
}
}

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

using System;
using System.Collections.Generic;
using System.IO;
using System.Web;
using Newtonsoft.Json;
using Zephyr.Utils; namespace Zephyr.Core
{
public class Exporter
{
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 static Exporter Instance()
{
var export = new Exporter();
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"]); return export;
} public Exporter Data(IDataGetter data)
{
_data = data.GetData(HttpContext.Current);
return this;
} public Exporter Data(string type)
{
var dataGetter = GetActor<IDataGetter>(_dataGetter, DEFAULT_DATAGETTER,type);
return Data(dataGetter);
} public Exporter Data(object data)
{
_data = data;
return this;
} public Exporter AddFormatter(string field,IFormatter formatter)
{
_fieldFormatter[field] = formatter;
return this;
} public Exporter Title(List<List<Column>> title)
{
_title = title;
return this;
} public Exporter FileName(string fileName)
{
_fileName = fileName;
return this;
} public Exporter Export(string type)
{
var export = GetActor<IExport>(_export, DEFAULT_EXPORT, type);
return Export(export);
} public Exporter Export(IExport export)
{
if (_title == null)
{
_title = new List<List<Column>>();
_title.Add(new List<Column>());
EachHelper.EachListHeader(_data, (i, field, type) => _title[].Add(new Column() { title = field, field = field, rowspan = , colspan = }));
} 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] : ;
var currentRow = ;
var currentCell = ; export.Init(_data); //生成多行题头
for (var i = ; i < _title.Count; i++)
{
currentCell = ; for (var j = ; 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 > )
export.MergeCell(currentCell, currentRow, currentCell + item.colspan - , currentRow + item.rowspan - ); if (!string.IsNullOrEmpty(item.field))
{
if (!fieldIndex.ContainsKey(item.field))
fieldIndex[item.field] = new List<int>();
fieldIndex[item.field].Add(currentCell);
} for (var k = ; k < item.colspan; k++)
currentHeadRow[currentCell] = GetCurrentHeadRow(currentCell++) + item.rowspan;
}
currentRow++;
} //设置题头样式
export.SetHeadStyle(, , currentCell - , currentRow - ); //设置数据样式
var dataCount = ;
EachHelper.EachListRow(_data, (i, r) => dataCount++);
export.SetRowsStyle(, currentRow, currentCell - , currentRow + dataCount - ); //填充数据
EachHelper.EachListRow(_data, (rowIndex, rowData) =>
{
EachHelper.EachObjectProperty(rowData, (i, name, value) =>
{
if (fieldIndex.ContainsKey(name))
foreach (int cellIndex in fieldIndex[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(string type)
{
var compress = GetActor<ICompress>(_compress, DEFAULT_COMPRESS, type);
return Compress(compress);
} public Exporter Compress(ICompress compress)
{
_fileStream = compress.Compress(_fileStream,string.Format("{0}.{1}",_fileName,_suffix));
_suffix = compress.Suffix(_suffix);
return this;
} public void Download()
{
if (_fileStream != null && _fileStream.Length > )
ZFiles.DownloadFile(HttpContext.Current, _fileStream, string.Format("{0}.{1}",_fileName,_suffix), * * );
} private T GetActor<T>(Dictionary<string, Type> dict, string defaultKey, string key)
{
if (!dict.ContainsKey(key))
key = defaultKey; return (T)Activator.CreateInstance(dict[key]);
}
}
}

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

IDataGetter   ApiData

IExport         XlsxExport XlsExport HtmlDocExport PdfExport

IComresss     ZipCompress

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

ApiData

using System;
using System.Dynamic;
using System.Linq;
using System.Web;
using Newtonsoft.Json; namespace Zephyr.Core
{
public class ApiData : IDataGetter
{
public object GetData(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]=get
var 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 > ? route[] : "Get";
var methodInfo = type.GetMethod(action);
var parameters = new object[] { new RequestWrapper().SetRequestData(param) };
data = methodInfo.Invoke(instance, parameters); if (data.GetType() == typeof(ExpandoObject))
{
if ((data as ExpandoObject).Where(x => x.Key == "rows").Count() > )
data = data.rows;
}
} return data;
}
}
}

XlsxExport 导出excel2007 利用 Epplus实现

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Drawing;
using Zephyr.Utils;
using Zephyr.Utils.EPPlus;
using Zephyr.Utils.EPPlus.Style; namespace Zephyr.Core
{
public class XlsxExport:IExport
{
public string suffix { get {return "xlsx"; } } private ExcelPackage package;
private ExcelWorksheet sheet; public void Init(object data)
{
package = new ExcelPackage();
sheet = package.Workbook.Worksheets.Add("sheet1");
} public void MergeCell(int x1,int y1,int x2,int y2)
{
sheet.Cells[y1+, x1+, y2+, x2+].Merge = true;
} public virtual void FillData(int x, int y,string field, object value)
{
if (ZGeneric.IsTypeIgoreNullable<DateTime>(value))
sheet.Cells[y + , x + ].Style.Numberformat.Format = "yyyy-MM-dd hh:mm:ss";
sheet.Cells[y + , x + ].Value = value;
} public virtual void SetHeadStyle(int x1, int y1, int x2, int y2)
{
using (var head = sheet.Cells[y1 + , x1 + , y2 + , x2 + ]) // set head style
{
head.Style.Font.Bold = true;
head.Style.Font.Size = ;
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, int y2)
{
using (var data = sheet.Cells[y1 + , x1 + , y2 + , x2 + ])// set data style
{
data.Style.Font.Name = "Arial";
data.Style.Font.Size = ; 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);
}
} public Stream SaveAsStream()
{
var ms = new MemoryStream();
package.SaveAs(ms); package = null;
sheet = null;
return ms;
}
}
}

XlsExport 导出excel2003 利用NPOI实现

using System.IO;
using Zephyr.Utils;
using Zephyr.Utils.NPOI.HSSF.UserModel;
using Zephyr.Utils.NPOI.SS.UserModel;
using Zephyr.Utils.NPOI.HSSF.Util;
using Zephyr.Utils.NPOI.SS.Util; namespace Zephyr.Core
{
public class XlsExport:IExport
{
public string suffix { get {return "xls"; } } private HSSFWorkbook workbook;
private ISheet sheet; public void Init(object data)
{
workbook = new HSSFWorkbook();
sheet = workbook.CreateSheet("sheet1");
sheet.DefaultRowHeight = * ;
} public void MergeCell(int x1,int y1,int x2,int y2)
{
CellRangeAddress range = new CellRangeAddress(y1, y2, x1, x2);
sheet.AddMergedRegion(range);
} public virtual void FillData(int x, int y,string field, object value)
{
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, ));
break;
default:
cell.SetCellValue(ZConvert.ToString(value));
break;
}
} public virtual void SetHeadStyle(int x1, int y1, int x2, int y2)
{
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, int y2)
{
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;
}
}
} public Stream SaveAsStream()
{
var ms = new MemoryStream();
workbook.Write(ms);
ms.Flush();
ms.Position = ; workbook = null;
sheet = null;
return ms;
} private ICellStyle 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 = ;//字号
font.Boldweight = ;//加粗
//font.Color = HSSFColor.WHITE.index;//颜色
headStyle.SetFont(font); return headStyle;
} private ICellStyle 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 = ;//字号
dataStyle.SetFont(datafont); return dataStyle;
}
}
}

PdfExport 利用Gios pdf实现

using System;
using System.Collections.Generic;
using System.IO;
using System.Drawing;
using Zephyr.Utils;
using Zephyr.Utils.Gios.Pdf;
using System.Data; namespace Zephyr.Core
{
public class PdfExport:IExport
{
public string suffix { get {return "pdf"; } } private DataTable table;
private List<string> title; public void Init(object data)
{ var type = ZGeneric.GetGenericType(data);
var tableName = ZGeneric.IsDynamicType(type) ? string.Empty : type.Name; table = new DataTable(tableName);
EachHelper.EachListHeader(data, (rowIndex, name, cellType) =>
{
string typeName = cellType.ToString();
if (cellType.IsGenericType)
typeName = cellType.GetGenericArguments()[].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,int y2)
{
throw new Exception("pdf未实现多选title");
} public virtual void FillData(int x, int y,string field, object value)
{
if (field.StartsWith("title_"))
{
title.Add(field.Split('_')[]);
return;
} if (table.Rows.Count< y)
table.Rows.Add(table.NewRow()); if (value != null && (Type.GetType(value.GetType().ToString(), false) != null))
table.Rows[y-][field] = value;
} public virtual void SetHeadStyle(int x1, int y1, int x2, int y2)
{ } public virtual void SetRowsStyle(int x1, int y1, int x2, int y2)
{ } public Stream SaveAsStream()
{
table.EndLoadData();
table.AcceptChanges();
var removes = new List<string>();
foreach (DataColumn dc in table.Columns)
if (title.IndexOf(dc.ColumnName) == -)
removes.Add(dc.ColumnName);
foreach(var name in removes)
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(, 29.7)); // Now we create a Table of 100 lines, 6 columns and 4 points of Padding.
PdfTable myPdfTable = myPdfDocument.NewTable(new Font("Arial", ), table.Rows.Count, table.Columns.Count, ); // 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, , 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[].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, , , , )); // we also put a Label
PdfTextArea pta = new PdfTextArea(new Font("Arial", , FontStyle.Bold), Color.Red
, new PdfArea(myPdfDocument, , , , ), 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 = new MemoryStream();
myPdfDocument.SaveToStream(stream);
return stream;
}
}
}

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

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text; namespace Zephyr.Core
{
public class HtmlDocExport:IExport
{
public string suffix { get {return "doc"; } } private StringBuilder sBuilder;
private int rowIndex;
private Dictionary<int,object> row; public void Init(object data)
{
rowIndex = ;
row = new Dictionary<int, object>();
sBuilder = new StringBuilder();
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,int y2)
{
throw new Exception("htmldoc未实现多选title");
} public virtual void FillData(int x, int y,string field, object value)
{
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, int y2)
{ } public virtual void SetRowsStyle(int x1, int y1, int x2, int y2)
{ } public Stream SaveAsStream()
{
AppendRow(row.OrderBy(m => m.Key).Select(m => m.Value).ToArray());
sBuilder.Append("</table"); byte[] byteArray = Encoding.Default.GetBytes(sBuilder.ToString());
var stream = new MemoryStream(byteArray);
return stream;
} private void AppendRow(object [] values)
{
sBuilder.Append("<tr>");
foreach(var value in values)
sBuilder.Append(string.Format("<td>{0}</td>", value??string.Empty ));
sBuilder.Append("</tr>");
}
}
}

ZipCompress利用Ionic.Zip实现压缩

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Zephyr.Utils.Ionic.Zip; namespace Zephyr.Core
{
public class ZipCompress: ICompress
{
public string Suffix(string orgSuffix)
{
return "zip";
} public Stream Compress(Stream fileStream,string fullName)
{
using (var zip = new ZipFile())
{
zip.AddEntry(fullName, fileStream);
Stream zipStream = new MemoryStream();
zip.Save(zipStream);
return zipStream;
}
}
}
}

Formatter实现示例

using System;

namespace Zephyr.Core
{
public class SexFormatter:IFormatter
{
public object Format(object value)
{
switch(Convert.ToString(value))
{
case "":
return "纯爷们";
case "":
return "女汉子";
default:
return "春哥";
}
}
}
}

实现了以上这样接口,那么这个Exporter类的功能基本就OK了,如果要拓展一些其它格式以及自定义一些只要实现这些接口就可以,使用很方便了。
那么我们就应用吧,MVC 及webform都可以,我使用的是mvc,在HomeController下添加一个Download的Action,只要添加一句代码就可以实现了

public void Download()
{
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;
return self;
}; this.compress = function () {
self.params.compressType = 'zip';
return self;
}; this.title = function (filed,title) {
self.params.titles[0][filed] = title;
return self;
}; 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')[];
var doc = downloadHelper.contentWindow.document;
if (doc) {
doc.open();
doc.write('')//微软为doc.clear()有时会出bug
doc.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[];
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 = ; 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);
else
self.params = $.extend(true, {}, defaultOptions, opt); return self;
};

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

功能测试

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

测试 html代码

<div id="dropdown" style="width:100px; display:none;">
<div data-options="iconCls:'icon-ext-xls'" data-bind="click:downloadClick1">Excel2003表格所有页</div>
<div data-options="iconCls:'icon-ext-xls'" data-bind="click:downloadClick2">Excel2003表格指定页</div>
<div data-options="iconCls:'icon-ext-rar'" data-bind="click:downloadClick3">Excel2003压缩zip</div>
<div data-options="iconCls:'icon-page_excel'" data-bind="click:downloadClick4">Excel2007/2010</div>
<div data-options="iconCls:'icon-ext-pdf'" data-bind="click:downloadClick5">PDF</div>
<div data-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(,).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");

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

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

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

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

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

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

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

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

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

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

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

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

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

1 导出excel2003 多行题头

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

2 导出excel2007 多行题头

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

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

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

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

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

<div id="dropdown" style="width:100px; display:none;">
<div data-options="iconCls:'icon-ext-xls'" suffix="xls" data-bind="click:downloadClick">Excel2003 </div>
<div data-options="iconCls:'icon-page_excel'" suffix="xlsx" data-bind="click:downloadClick">Excel2007 </div>
<div data-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地址被认为是广告,被删除了)
我有好长一段时间没写博客了。今天正好有点时间就把我们框架中的导出功能跟大家分享一下。

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