通用的web系統數據導出功能設計實現(導出excel2003/2007 word pdf zip等)


前言

我們在做web系統中,導出也是很常用的一個功能,如果每一個數據列表都要對應寫一個導出的方法不太現實。現在就想設計一個共通的功能來實現這個導出。

需求分析

在開始之前我們先要明白我們要實現怎樣一個功能
1、支持導出excel2003/excel2007 word pdf等文件格式
2、支持數據分頁,可以導出全部頁或指定頁
3、支持導出的文檔再壓縮zip rar 7z
4、支持導出多行的題頭
5、支持格式化,如將 0、1 轉換為 男、女
5、可拓展、可自定義等
image

 

技術分析設計

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類也挺好用的了,那么接下來我們再來談談如何具體實現吧

 

技術實現

設計時我們先實現后台
一、后台其實我已經實現出來了,截個圖給大家看看先
image
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 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]);
        }
    }
}

 

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 > 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中只需要這么簡單的一句代碼就搞定了。我們繼續看前台吧

二、前台實現
前台只有一段共通的腳本,很簡單

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中有提到導出功能,但都只有上面的一句代碼。估計大家看了這篇博客才能明白。


后述

已經把這個材料管理系統掛在我們的網站上了。(demo地址被認為是廣告,被刪除了)
我有好長一段時間沒寫博客了。今天正好有點時間就把我們框架中的導出功能跟大家分享一下。
如果大家喜歡就幫[推薦]一下吧,大家的支持才是我寫博客的動力。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM