运用Aspose.Cells组件将数据导至Execl

  工作了几年,却一直没有做过将数据导出至Execl,想想也很奇葩。

  最近我们在做一个供应链系统,这类系统,里面自然会涉及到操作Execl的功能,正好我所负责的功能里面有这个。

  导出Execl的需求大概是这样的,一个主表,一个从表,关系是一对多,主从表关联,导出的数据是主表有多少条数据,那么Execl里面就有多少行数据,这里自然会涉及到合并单元格的动作,并且数据里面还有图片列,但看这需求总结出两点:1) 合并单元格,2) 显示图片。

  最后导出的效果图截取部分:

  

  部门里面的架构组提供的导出Execl组建不支持对指定列合并单元格功能,依赖于NOPI,所以后来选择了Aspose.Cells这玩意,听说网上代码一大堆,就找了找,不过么有找到合适的,之后就去官网下了示例代码看了一些,有些代码片段还是有参考价值的。

  下面就说自己的思路了。

  1.原始数据准备

    因为主从关系,且导出行数以主表数据行数决定,所以这里用到了oracle里面的行列转换函数wm_concat,其要点是:

select id, wm_concat(to_char(colorname)) from tb group by id

    因为colorname是中文,如果不加to_char函数的话,查询出来的数据会是乱码。

  2. 图片显示与合并单元格

    因为这里的图片都存在图片服务器上面,这个组件不支持网络图片,需要先将图片转换成MemoryStream,我觉得吧,这个很鸡肋。

方法是sheet.Pictures.Add,前两个参数是起始行列,行列索引默认是从1开始的,这个需要注意下。合并单元格的方法就是sheet.Cells.Merge,前两个参数是起始行列,索引是从0开始,不知为什么要这样设计?

    还有一个我觉得很不友好的是,给单元格加边框的时候竟然分上下左右4个部分加边线,很是奇怪,下面有代码。

  3. 代码设计

    这里设计了一个接口IExeclDataExporter,里面只有一个方法void DataExport(ExporterParams exporterParams); 代码如下:

  /// <summary>
    /// Execl数据导出服务(依赖于Aspose.Cells组件)
    /// </summary>
    internal interface IExeclDataExporter
    {
        void DataExport(ExporterParams exporterParams);
    }

    /// <summary>
    /// 参数
    /// </summary>
    internal sealed class ExporterParams
    {
        /// <summary>
        /// Execl列头
        /// </summary>
        public IList<DataHeader> ExeclDataHeader { get; set; }

        /// <summary>
        /// Execl数据体
        /// </summary>
        public DataTable ExeclDataBody { get; set; }

        public string ExeclTitle { get; set; }

        /// <summary>
        /// 不需要合并列
        /// </summary>
        public string NonMergeColumnCode { get; set; }

        /// <summary>
        /// 图片列
        /// </summary>
        public string ImageColumnCode { get; set; }

        /// <summary>
        /// 导出(绝对路径)
        /// </summary>
        public string DocumentFileName { get; set; }
    }

    /// <summary>
    /// 列头
    /// </summary>
    internal sealed class DataHeader
    {
        /// <summary>
        /// 列显示名称
        /// </summary>
        public string DisplayName { get; set; }

        /// <summary>
        /// 列编码
        /// </summary>
        public string ColumnCode { get; set; }

        /// <summary>
        /// 列宽度
        /// </summary>
        public int ColumnWidth { get; set; }
    }

    实现该接口的类是ExeclDataExporter,代码是:

internal class ExeclDataExporter:IExeclDataExporter
    {
        public void DataExport(ExporterParams eParams)
        {
            Guard.AgainstNull(eParams.ExeclDataHeader, "ExeclDataHeader");

            //1. 变量定义,样式
            Workbook workbook = new Workbook(); 
            DefineStyle(workbook);

            //2. 设置表头数据
            SetExeclHeadData(workbook, eParams.ExeclTitle, eParams.ExeclDataHeader);

            //3. 设置表体数据
            SetExeclBodyData(workbook, eParams);

            //4. 保存
            workbook.Save(eParams.DocumentFileName);

        }

        private void SetExeclHeadData(Workbook workbook, string title, IList<DataHeader> ExeclDataHeader)
        {
            Worksheet sheet = workbook.Worksheets[0];

            sheet.Cells.Merge(0, 0, 1, ExeclDataHeader.Count);
            sheet.Cells[0, 0].PutValue(title);

            Style style1 = workbook.Styles["Font25Center"];
            sheet.Cells[0, 0].SetStyle(style1);

            Style style2 = workbook.Styles["FontTitleCenter"];
            for (var i = 0; i < ExeclDataHeader.Count; i++)
            {
                var head = ExeclDataHeader[i];

                sheet.Cells.SetColumnWidth(i, head.ColumnWidth);

                sheet.Cells[1, i].PutValue(head.DisplayName);
                sheet.Cells[1, i].SetStyle(style2);
            }

            //设置当前数据行索引
            CurrentRowIndex = 2;
        }

        private void SetExeclBodyData(Workbook workbook, ExporterParams eParams)
        {
            var dtBody = eParams.ExeclDataBody;

            Style style = workbook.Styles["FontCenter"];

            for (var i = 0; i < dtBody.Rows.Count; i++)
            {
                var row = dtBody.Rows[i];

                //填充行数据
                FillRowData(workbook.Worksheets[0], style, dtBody.Rows[i], eParams);
            }
        }

        private void FillRowData(Worksheet sheet, Style style, DataRow row, ExporterParams eParams)
        {
            //上一次当前行索引
            var oldCurrentRowIndex = CurrentRowIndex;

            //不合并列索引
            var nonMergeColumnIndex = GetColumnIndex(eParams.NonMergeColumnCode, eParams.ExeclDataHeader);

            //图片列索引
            var imageColumnIndex = GetColumnIndex(eParams.ImageColumnCode, eParams.ExeclDataHeader);

            //合并行的数据
            var mergeRowArr = row[eParams.NonMergeColumnCode] == DBNull.Value ? new string[0] : row[eParams.NonMergeColumnCode].ToString().Split(',');

            //重新设置当前行索引
            CurrentRowIndex = mergeRowArr.Length == 0 ? CurrentRowIndex + 1 : CurrentRowIndex + mergeRowArr.Length;

            for (var j = 0; j < eParams.ExeclDataHeader.Count; j++)
            {
                var head = eParams.ExeclDataHeader[j];

                var obj = row[head.ColumnCode] == DBNull.Value ? "" : row[head.ColumnCode];

                //需要合并
                if (mergeRowArr.Length >= 1)
                {
                    if (j != nonMergeColumnIndex)
                    {
                        //合并
                        sheet.Cells.Merge(oldCurrentRowIndex, j, mergeRowArr.Length, 1);

                        if (j == imageColumnIndex)
                        {
                            if (!string.IsNullOrWhiteSpace(obj.ToString()))
                            {
                                var ms = GetMemoryStream(obj.ToString());
                                if (ms != null)
                                {
                                    sheet.Cells.SetRowHeight(oldCurrentRowIndex, 45);
                                    int index = sheet.Pictures.Add(oldCurrentRowIndex, j, ms, 100, 100);
                                    SetImagePosition(sheet, index);
                                }
                            }
                            SetCellStyle(sheet, style, oldCurrentRowIndex, j);
                        }
                        else
                        {
                            //赋值
                            sheet.Cells[oldCurrentRowIndex, j].PutValue(obj);

                            for (var k = 0; k < mergeRowArr.Length; k++)
                            {
                                SetCellStyle(sheet, style, oldCurrentRowIndex + k, j);
                            }
                        }
                    }
                    else
                    {
                        for (var k = 0; k < mergeRowArr.Length; k++)
                        {
                            sheet.Cells[oldCurrentRowIndex + k, j].PutValue(mergeRowArr[k]);
                            SetCellStyle(sheet, style, oldCurrentRowIndex + k, j);
                        }
                    }
                }
                else
                {
                    if (j == imageColumnIndex)
                    {
                        var ms = GetMemoryStream(obj.ToString());
                        if (ms != null)
                        {
                            sheet.Cells.SetRowHeight(oldCurrentRowIndex, 45);
                            int index = sheet.Pictures.Add(oldCurrentRowIndex, j, ms);
                            SetImagePosition(sheet, index);
                        }

                    }
                    else
                    {
                        sheet.Cells[oldCurrentRowIndex, j].PutValue(obj);
                    }

                    SetCellStyle(sheet, style, oldCurrentRowIndex, j);
                }
            }
        }

        private void SetCellStyle(Worksheet sheet, Style style, int k, int j)
        {
            sheet.Cells[k, j].SetStyle(style);
        }

        private void SetImagePosition(Worksheet sheet, int index)
        {
            Aspose.Cells.Drawing.Picture pic = sheet.Pictures[index];
            pic.Left = 6;
            pic.Top = 2;
        }

        private void DefineStyle(Workbook workbook)
        {
            Style style;
            int styleIndex = workbook.Styles.Add();
            style = workbook.Styles[styleIndex];
            style.Font.Size = 25;
            style.Font.IsBold = true;
            style.HorizontalAlignment = TextAlignmentType.Center;
            style.Borders[BorderType.LeftBorder].Color = Color.Black;
            style.Borders[BorderType.LeftBorder].LineStyle = CellBorderType.Thin;
            style.Borders[BorderType.RightBorder].Color = Color.Black;
            style.Borders[BorderType.RightBorder].LineStyle = CellBorderType.Thin;
            style.Borders[BorderType.TopBorder].Color = Color.Black;
            style.Borders[BorderType.TopBorder].LineStyle = CellBorderType.Thin;
            style.Borders[BorderType.BottomBorder].Color = Color.Black;
            style.Borders[BorderType.BottomBorder].LineStyle = CellBorderType.Thin;
            style.Name = "Font25Center";

            styleIndex = workbook.Styles.Add();
            style = workbook.Styles[styleIndex];
            style.Font.IsBold = true;
            style.HorizontalAlignment = TextAlignmentType.Center;
            style.Borders[BorderType.LeftBorder].Color = Color.Black;
            style.Borders[BorderType.LeftBorder].LineStyle = CellBorderType.Thin;
            style.Borders[BorderType.RightBorder].Color = Color.Black;
            style.Borders[BorderType.RightBorder].LineStyle = CellBorderType.Thin;
            style.Borders[BorderType.TopBorder].Color = Color.Black;
            style.Borders[BorderType.TopBorder].LineStyle = CellBorderType.Thin;
            style.Borders[BorderType.BottomBorder].Color = Color.Black;
            style.Borders[BorderType.BottomBorder].LineStyle = CellBorderType.Thin;
            style.Name = "FontTitleCenter";

            styleIndex = workbook.Styles.Add();
            style = workbook.Styles[styleIndex];
            style.HorizontalAlignment = TextAlignmentType.Center;
            style.IsTextWrapped = true;
            style.Borders[BorderType.LeftBorder].Color = Color.Black;
            style.Borders[BorderType.LeftBorder].LineStyle = CellBorderType.Thin;
            style.Borders[BorderType.RightBorder].Color = Color.Black;
            style.Borders[BorderType.RightBorder].LineStyle = CellBorderType.Thin;
            style.Borders[BorderType.TopBorder].Color = Color.Black;
            style.Borders[BorderType.TopBorder].LineStyle = CellBorderType.Thin;
            style.Borders[BorderType.BottomBorder].Color = Color.Black;
            style.Borders[BorderType.BottomBorder].LineStyle = CellBorderType.Thin;
            style.Name = "FontCenter";

        }

        #region 辅助方法

        private int GetColumnIndex(string columnCode, IList<DataHeader> ExeclDataHeader)
        {
            var item = ExeclDataHeader.FirstOrDefault(w => w.ColumnCode == columnCode);

            return ExeclDataHeader.IndexOf(item);
        }

        private MemoryStream GetMemoryStream(string url)
        {
            try
            {
                WebClient wc = new WebClient();
                byte[] bytes = wc.DownloadData(url);
                return new MemoryStream(bytes);
            }
            catch
            {
                return null;
            }
        }

        #endregion

        #region 私有属性

        private int CurrentRowIndex { set; get; } 

        #endregion
    }

     调用代码如下:

      var data = GetExportProductInfo(querymodel).Data; //这里是datatable
            
            if (data.Rows.Count == 0)
                return null;

            var exporterParams = new ExporterParams();
            exporterParams.ExeclDataHeader = new List<DataHeader>() { 
                new DataHeader { DisplayName = "样版编号", ColumnCode = "SampleCode", ColumnWidth = 20 },
                new DataHeader { DisplayName = "图片", ColumnCode = "SmallPicStyle",  ColumnWidth = 8 },
                new DataHeader { DisplayName = "颜色", ColumnCode = "ColorName", ColumnWidth = 15 },
            };

            exporterParams.DocumentFileName = filePath;
            exporterParams.ExeclDataBody = data;
            exporterParams.NonMergeColumnCode = "ColorName";
            exporterParams.ImageColumnCode = "SmallPicStyle";
            exporterParams.ExeclTitle = "产品资料列表";

            IExeclDataExporter exporter = new ExeclDataExporter();
            exporter.DataExport(exporterParams);

      你们看了上面的代码是不是觉得很简单啊,呵呵,个人觉得吧,在合并单元格的计算逻辑需要注意一下,其他的都是搬砖的活,如果有什么疑问可以给我留言,写的不是很好请勿见怪。

 

  

posted @ 2013-05-10 21:45  布衣人老白  阅读(599)  评论(0编辑  收藏  举报