一个Excel导出类的实现过程(三):NPOI组件

NPOI组件强大高效,这里只使用它完成我们的Excel写入功能,需要更多内容则请自行搜索。

  • IWorkbook:工作簿对象,可以由NPOI.HSSF.UserModel.HSSFWorkbook实例化得来,Write(Stream stream)如其意;
  • ISheet:工作表对象,可以由IWorkbook实例的CreateSheet()方法创建;
  • IRow:行对象,可以由ISheet实例的Create()方法创建;
  • ICell:单元格对象,可以由IRow实例的CreateCell()方法创建。

先处理翻页问题,10版Excel最多支持1048576行,03版Excel最多支持65536行。只处理03版Excel,除去表头,翻页算法如下:

IWorkbook workbook = new HSSFWorkbook();
ISheet sheet = null;
const Int32 RowPerSheet = 65535;

for (Int32 r = 0; r < records.Count; r++)
{
    if ((r % RowPerSheet) == 0)
    {
        Int32 sheetIndex = (Int32)((Double)r / RowPerSheet) + 1;
        sheet = workbook.CreateSheet("Sheet" + sheetIndex);

        for (Int32 i = 0; i < headers.Count; i++)
        {
            Console.Write(headers[i].PrintName);
            Console.Write("\t");
        }
        Console.WriteLine();
    }

    //注意CreateRow(Int32 rownum)中参数rownum虽然从第0行开始,但因为表头存在,每次得往下一行
    IRow row = sheet.CreateRow(r % RowPerSheet + 1);
    for (Int32 i = 0; i < props.Length; i++)
    {
        //拿row干活儿了...
    }
}

注意引用NPOI.SS.UserModel和NPOI.HSSF.UserModel命名空间,现在我们有Row,那么创建单元格写入内容就容易了。

考虑到导出一个65536行的工作表,看的人会抓狂,于是封装导出方法到ExcelHelper类,提供行数属性及导出方法,同时将Header类设置为其内部类(这个不是必须的,但重命名一下可能没错),实现如下:

public class ExcelHelper
{
    public class Header
    {
        public String Name { get; private set; }
        public String PrintName { get; private set; }

        public Header(String name)
            : this(name, name)
        {
        }

        public Header(String name, String printName)
        {
            Name = name;
            PrintName = printName;
        }
    }

    private const Int32 MaxRowPerSheet = 65535;
    private Int32 rowPerSheet = 1000;
    public Int32 RowPerSheet
    {
        get { return rowPerSheet; }
        set
        {
            if (value < 0 || value > MaxRowPerSheet)
            {
                throw new ArgumentOutOfRangeException("RowPerSheet");
            }
            else
            {
                rowPerSheet = value;
            }
        }
    }

    public IWorkbook Export<T>(IList<T> records)
    {
        if (records == null)
            throw new ArgumentNullException("records");

        String[] headers = typeof(T).GetProperties().Select(p => p.Name).ToArray();
        return Export<T>(records, headers);
    }

    public IWorkbook Export<T>(IList<T> records, IList<String> headers)
    {
        if (records == null)
            throw new ArgumentNullException("records");
        if (headers == null || headers.Count == 0)
            throw new ArgumentNullException("headers");

        Header[] newHeaders = typeof(T).GetProperties().Select(p => new Header(p.Name)).ToArray();
        return Export<T>(records, newHeaders);
    }

    public IWorkbook Export<T>(IList<T> records, IList<Header> headers)
    {
        if (records == null)
            throw new ArgumentNullException("records");
        if (headers == null || headers.Count == 0)
            throw new ArgumentNullException("headers");

        PropertyInfo[] props = new PropertyInfo[headers.Count];
        for (int i = 0; i < headers.Count; i++)
        {
            props[i] = typeof(T).GetProperty(headers[i].Name); //注意属性数组仍然可以有元素为null
        }

        IWorkbook workbook = new HSSFWorkbook();
        ISheet sheet = null;
        IRow row = null;

        for (int r = 0; r < records.Count; r++)
        {
            if ((r % RowPerSheet) == 0)
            {
                Int32 sheetIndex = (Int32)((Double)r / RowPerSheet) + 1;
                sheet = workbook.CreateSheet("Sheet" + sheetIndex);
                row = sheet.CreateRow(0);
                for (int i = 0; i < headers.Count; i++)
                {
                    row.CreateCell(i).SetCellValue(headers[i].PrintName);
                }
                Console.WriteLine();
            }

            //注意CreateRow(Int32 rownum)中参数rownum虽然从第0行开始,但因为表头存在,每次得往下一行
            row = sheet.CreateRow(r % RowPerSheet + 1);
            for (int i = 0; i < props.Length; i++)
            {
                if (props[i] != null) //注意null检查
                {
                    Object value = props[i].GetValue(records[r], null);
                    if (value != null)
                    {
                        row.CreateCell(i).SetCellValue(value.ToString());
                    }
                }
            }
        }

        for (Int32 i = 0; i < workbook.NumberOfSheets; i++)
        {
            sheet = workbook.GetSheetAt(i);
            for (Int32 h = 0; h < headers.Count; h++)
            {
                sheet.AutoSizeColumn(h); //每列宽度自适应
            }
        }
        return workbook;
    }
}

客户端调用如下:

static void Main(string[] args)
{
    List<Person> persons = new List<Person>();
    //persons.Add(new Person { ID = 1, Name = "Rattz", Birth = new DateTime(1980, 10, 1), Salary = 20.2D });
    //persons.Add(new Person { ID = 2, Name = "Mike", Birth = new DateTime(1988, 2, 15), Salary = 20.2D });

    Int32 records = 201;
    for (Int32 i = 0; i < records; i++)
    {
        persons.Add(new Person { ID = i, Name = "name" + i, Birth = new DateTime(1980, 10, 1), Salary = 20.2D });
    }

    List<ExcelHelper.Header> headers = new List<ExcelHelper.Header>();
    headers.Add(new ExcelHelper.Header("ID"));
    headers.Add(new ExcelHelper.Header("Name", "名称"));
    headers.Add(new ExcelHelper.Header("Birth", "生日"));
    headers.Add(new ExcelHelper.Header("Salary", "薪水"));

    ExcelHelper excelHelper = new ExcelHelper();
    excelHelper.RowPerSheet = 100;
    IWorkbook workbook = excelHelper.Export<Person>(persons, headers);

    String path = @"d:\1.xls";
    using (FileStream stream = File.Open(path, FileMode.OpenOrCreate))
    {
        workbook.Write(stream);
    }
}

至此功能已完成,但细节待完善,观察“生日”列所有值都是1980-10-01 00:00:00,这是单元格值设置语句row.CreateCell(i).SetCellValue(value.ToString())过于简单的缘故,后续一篇继续,读者也可以参考NPOI组件的ICell属性与方法自行完成。

posted @ 2013-05-16 16:32  Jusfr  阅读(1735)  评论(3编辑  收藏  举报