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

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

前言

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

需求分析

在开始以前咱们先要明白咱们要实现怎样一个功能 
一、支持导出excel2003/excel2007 word pdf等文件格式 
二、支持数据分页,能够导出所有页或指定页 
三、支持导出的文档再压缩zip rar 7z 
四、支持导出多行的题头 
五、支持格式化,如将 0、1 转换为 男、女 
五、可拓展、可自定义等 
imageios

 

技术分析设计

一、生成文件基本可使用开源的组件实现 
excel2003 使用 NPOI (有网友提醒我npoi已经支持2007了,我这里暂时仍是只处理2003) 
excel2007 使用 EPPlus 
word        使用 DocX 还可使用直接使用html table导出假的word 
pdf          使用 Gios Pdf 
其它格式预留接口…web


二、B/S系统要分先后台,先看前台设计 
能够定义一个共通的js方法如com.exporter那么咱们前台使用时应该要能够这样用:api

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();
复制代码

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


三、后台部分设计 
一样咱们从调用的地方入手,在个人下载请求的方法中入手:app

    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方法oop

public Exporter Data(IDataGetter data)
{
 
}

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

复制代码
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类也挺好用的了,那么接下来咱们再来谈谈如何具体实现吧

 

技术实现

设计时咱们先实现后台 
1、后台其实我已经实现出来了,截个图给你们看看先 
image 
一、首先咱们定义一些基本的元素 
定义题头结构

复制代码
    public class Column
    {
        public Column()
        {
            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; }
    }
复制代码

定义压缩接口

复制代码
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);
    }
}
复制代码

 

二、基本的元素都定义好了,咱们再来实现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[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 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 > 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, string key)
        {
            if (!dict.ContainsKey(key))
                key = defaultKey;

            return (T)Activator.CreateInstance(dict[key]);
        }
    }
}
复制代码

 

三、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 > 2 ? route[2] : "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() > 0)
                        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+1, x1+1, y2+1, x2+1].Merge = true;
        }

        public virtual void FillData(int x, int y,string field, object value)
        {
            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, int y2)
        {
            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, int y2)
        {
            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);
            }
        }

        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 = 200 * 20;
        }

        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, 0));
                    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 = 0;

            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 = 12;//字号
            font.Boldweight = 600;//加粗
            //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 = 11;//字号
            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()[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,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('_')[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, 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) == -1)                    
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(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 Label 
                PdfTextArea 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 = 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 = 0;
            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 "0":
                    return "纯爷们";
                case "1":
                    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中只须要这么简单的一句代码就搞定了。咱们继续看前台吧

2、前台实现 
前台只有一段共通的脚本,很简单

复制代码
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')[0];
        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[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);
    else
        self.params = $.extend(true, {}, defaultOptions, opt);

    return self;
};
复制代码

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


功能测试


image

测试 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(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"); 
image

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

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

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

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

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

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

1 导出excel2003 多行题头 
image

2 导出excel2007 多行题头 
image

还有一些其它的功能我就不一一测试了,你们有兴趣能够本身再研究

在咱们的材料管理的系统中导出通常只用几个选项,1 导出所有页excel2003 2 导出所有页excel2007/2010 3导出所有页word 这三个 
image

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

复制代码
<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中有提到导出功能,但都只有上面的一句代码。估计你们看了这篇博客才能明白。

相关文章
相关标签/搜索