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

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张图片

 

技术分析设计

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等)_第2张图片 
1、首先我们定义一些基本的元素 
定义题头结构

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

 

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 _defaltExport = "xls";
        const string _defaltDataGetter = "api";
        const string _defaltCompress = "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, _defaltDataGetter,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, _defaltExport, 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, _defaltCompress, 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]);
        }
    }
}
复制代码

 

3、Exporter类实现之后我们还要实现一些接口才能使用 
我们这里就先实现 
IDataGetter   ApiData 
IExport         XlsxExport XlsExport HtmlDocExport PdfExport 
IComresss     ZipComrepress 
其它的以后再实现就可以了,很方便 

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,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中只需要这么简单的一句代码就搞定了。我们继续看前台吧

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

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

    return self;
};
复制代码

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


功能测试


web系统数据导出功能设计实现(导出excel2003/2007 word pdf zip等)_第3张图片

测试 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"); 
web系统数据导出功能设计实现(导出excel2003/2007 word pdf zip等)_第4张图片

2 Excel2003表格指定页  com.exporter(self.grid).page(2,3).download("xls"); 
web系统数据导出功能设计实现(导出excel2003/2007 word pdf zip等)_第5张图片 

3 Excel2003压缩zip com.exporter(self.grid).compress('zip').download("xls"); 
web系统数据导出功能设计实现(导出excel2003/2007 word pdf zip等)_第6张图片

4 Excel2007/2010  com.exporter(self.grid).download("xlsx"); 
web系统数据导出功能设计实现(导出excel2003/2007 word pdf zip等)_第7张图片

5 PDF导出 com.exporter(self.grid).download("pdf"); Gios这个组件出现中文会乱码,我想了很多办法都解决不了,没办法,可能要找一另一个pdf组件,先隐藏中文的字段截个图给大家看吧 
web系统数据导出功能设计实现(导出excel2003/2007 word pdf zip等)_第8张图片

6 Word导出 com.exporter(self.grid).download("doc"); 
web系统数据导出功能设计实现(导出excel2003/2007 word pdf zip等)_第9张图片

测试导出多行题头,比如这个页面是多行的 
web系统数据导出功能设计实现(导出excel2003/2007 word pdf zip等)_第10张图片

1 导出excel2003 多行题头 
web系统数据导出功能设计实现(导出excel2003/2007 word pdf zip等)_第11张图片

2 导出excel2007 多行题头 
web系统数据导出功能设计实现(导出excel2003/2007 word pdf zip等)_第12张图片

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

在我们的材料管理的系统中导出一般只用几个选项,1 导出全部页excel2003 2 导出全部页excel2007/2010 3导出全部页word 这三个 
web系统数据导出功能设计实现(导出excel2003/2007 word pdf zip等)_第13张图片

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

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


后述

前段时间忙着用wordpress搭了个网站www.zoewin.com,已经把这个材料管理系统挂在我们的网站上了。 
我有好长一段时间没写博客了。今天正好有点时间就把我们框架中的导出功能跟大家分享一下。 
如果大家喜欢就帮一下推荐吧,大家的支持才是我写博客的动力。

你可能感兴趣的:(excel2003)