书接上文

以前的代码能跑就行,回头看自己都懵圈

这次依旧优化古董级代码

以下是Excel导出代码优化

/// <summary>
/// Excel导出
/// </summary>
/// <typeparam name="T">实体类</typeparam>
/// <param name="excelConfig">配置项</param>
/// <returns></returns>
public static MemoryStream ExportExcel<T>(ExcelConfig<T> excelConfig)
{
    var stream = new MemoryStream();
    // 非商业用途
    ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
    using (ExcelPackage package = new ExcelPackage(stream))
    {
        // 添加worksheet
        ExcelWorksheet worksheet = package.Workbook.Worksheets.Add(excelConfig.SheetName);
        // 单元格自动适应大小(好像没用还得手动设置列宽)
        // worksheet.Cells.Style.ShrinkToFit = true;
        for (int i = 0; i < excelConfig.ColumnNames.Count; i++)
        {
            // 设置列名
            worksheet.Cells[1, i + 1].Value = excelConfig.ColumnNames[i];
            // 设置列宽
            worksheet.Column(i + 1).Width = excelConfig.ColumnWidths[i];
        }
        // 金额格式
        // worksheet.Column(2).Style.Numberformat.Format = "¥#,##0.00";
        // 从第二行第三列到第一万行第三列被设置为下拉框
        // var unitmeasure = worksheet.DataValidations.AddListValidation(worksheet.Cells[2, 3, 10000, 3].Address);
        // unitmeasure.Formula.Values.Add("现金");
        // unitmeasure.Formula.Values.Add("刷卡");
        // 列下标
        int cellCount = 0;
        // 获得该类的Type
        Type t = excelConfig.DataList.FirstOrDefault().GetType();
        for (int i = 0; i < excelConfig.DataList.Count; i++)
        {
            // 遍历属性值
            foreach (PropertyInfo pi in t.GetProperties())
            {
                // 从第二行开始插入数据,因为第一行是列名
                var value = pi.GetValue(excelConfig.DataList[i]).ToString();
                worksheet.Cells[i + 2, ++cellCount].Value = value;
            }
            // 重置
            cellCount = 0;
        }
        // 设置是否进行锁定
        worksheet.Protection.IsProtected = true;
        // 设置密码
        worksheet.Protection.SetPassword("123456");
        // 下面是一些锁定时权限的设置
        worksheet.Protection.AllowAutoFilter = false;
        worksheet.Protection.AllowDeleteColumns = false;
        worksheet.Protection.AllowDeleteRows = false;
        worksheet.Protection.AllowEditScenarios = false;
        worksheet.Protection.AllowEditObject = false;
        worksheet.Protection.AllowFormatCells = false;
        worksheet.Protection.AllowFormatColumns = false;
        worksheet.Protection.AllowFormatRows = false;
        worksheet.Protection.AllowInsertColumns = false;
        worksheet.Protection.AllowInsertHyperlinks = false;
        worksheet.Protection.AllowInsertRows = false;
        worksheet.Protection.AllowPivotTables = false;
        worksheet.Protection.AllowSelectLockedCells = false;
        worksheet.Protection.AllowSelectUnlockedCells = false;
        worksheet.Protection.AllowSort = false;
        package.Save();
    }
    stream.Position = 0;
    return stream;
}

对于一些列的下拉框设置,后续还有优化空间

关于锁定和设置密码以及其他设置可以自行百度

Excel配置项

/// <summary>
/// Excel配置项
/// </summary>
/// <typeparam name="T"></typeparam>
public class ExcelConfig<T>
{
    /// <summary>
    /// Sheet名称
    /// </summary>
    public string SheetName { get; set; }

    /// <summary>
    /// 列名集合
    /// </summary>
    public List<string> ColumnNames { get; set; }

    /// <summary>
    /// 列宽集合
    /// </summary>
    public List<int> ColumnWidths { get; set; }

    /// <summary>
    /// 数据集
    /// </summary>
    public List<T> DataList { get; set; }
}

模拟调用

ExcelConfig<Student> studentList = new ExcelConfig<Student>();
studentList.SheetName = "学生报表";
// 正常情况从数据库取
studentList.DataList = new List<Student>();
studentList.ColumnNames = new List<string>
{
    "Id",
    "编号",
    "姓名",
    "等级",
    "专业",
    "地址",
};
// 列名集合与列宽集合数量须保持一直,为对应列的宽度
studentList.ColumnWidths = new List<int> { 10, 25, 15, 30, 15, 25 };
// 获取文件流后自行处理返回值
var steam = ExcelHelper.ExportExcel(studentList);

通过简单配置以及调用即可获取文件流

拿到文件流后即可返回给前端下载

以下是PDF导出代码优化

/// <summary>
/// PDF导出
/// </summary>
/// <typeparam name="T">实体类</typeparam>
/// <param name="pdfConfig">配置项</param>
/// <returns></returns>
public static byte[] ExportPDF<T>(PDFConfig<T> pdfConfig)
{
    // 创建一个文档对象
    Document document = new Document();
    // 创建一个 PDF 文件流
    string fileName = "测试.pdf";
    FileStream fileStream = new FileStream(fileName, FileMode.Create);
    // 实例化一个 PDF写入器
    PdfWriter.GetInstance(document, fileStream);
    document.Open();
    // 指定字体文件,IDENTITY_H:支持中文
    string fontpath = @"C:\Windows\Fonts\SIMHEI.TTF";
    BaseFont customfont = BaseFont.CreateFont(fontpath, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
    // 设置字体颜色样式
    var baseFont = new Font(customfont)
    {
        // 字体颜色
        Color = new BaseColor(0, 0, 0),
        // 字体大小
        Size = 8
    };

    // 定义字体样式
    var headerStyle = new Font(customfont)
    {
        Color = new BaseColor(0, 0, 0),
        Size = 18,
    };

    // 设置标题名称样式以及居中
    var head = new Paragraph(pdfConfig.SiteName, headerStyle);
    head.SetAlignment("Center");

    var headerStyle2 = new Font(customfont)
    {
        Color = new BaseColor(0, 0, 0),
        Size = 10,
    };
    var para = new Paragraph(string.Format("{0}", pdfConfig.TaskName), headerStyle2)
    {
        // 和报表对齐需要减少的左边距
        IndentationLeft = -30f
    };

    // 上方文字与表格相隔间距(空格不能为空,不然间距无效果)
    var placeholder = new Paragraph("   ", headerStyle2);

    PdfPTable tableRow_2 = new PdfPTable(8);
    tableRow_2.TotalWidth = 580f;
    tableRow_2.LockedWidth = true;
    tableRow_2.DefaultCell.Border = Rectangle.NO_BORDER;
    tableRow_2.WidthPercentage = 100;
    tableRow_2.DefaultCell.MinimumHeight = 80f;
    tableRow_2.SetWidths(pdfConfig.ColumnWidths);

    for (int i = 0; i < pdfConfig.ColumnNames.Count; i++)
    {
        var Row_2_Cell_i = new PdfPCell(new Paragraph(pdfConfig.ColumnNames[i], baseFont));
        Row_2_Cell_i.HorizontalAlignment = Element.ALIGN_CENTER;// 文字居中
        Row_2_Cell_i.BackgroundColor = BaseColor.LightGray;
        tableRow_2.AddCell(Row_2_Cell_i);
    }

    document.Add(head);

    // 添加一张图片到文档
    // Image image = Image.GetInstance("cute.gif");
    // document.Add(image);

    document.Add(placeholder);
    document.Add(para);
    document.Add(placeholder);
    document.Add(tableRow_2);

    // 获得该类的Type
    Type t = pdfConfig.DataList.FirstOrDefault().GetType();

    // 填充数据
    for (int i = 0; i < pdfConfig.DataList.Count; i++)
    {
        PdfPTable tableRow_3 = new PdfPTable(8);
        tableRow_3.TotalWidth = 580f;
        tableRow_3.LockedWidth = true;
        tableRow_3.DefaultCell.Border = Rectangle.NO_BORDER;
        tableRow_3.WidthPercentage = 100;
        tableRow_3.DefaultCell.MinimumHeight = 80f;
        tableRow_3.SetWidths(pdfConfig.ColumnWidths);
        // 遍历属性值
        foreach (PropertyInfo pi in t.GetProperties())
        {
            var value = pi.GetValue(pdfConfig.DataList[i]).ToString();
            var txt = new Paragraph(value, baseFont);
            var cell = new PdfPCell(txt);
            tableRow_3.AddCell(cell);
        }
        document.Add(tableRow_3);
    }

    document.Close();
    fileStream.Close();
    fileStream.Dispose();
    return GetFileStream(fileName);
}

/// <summary>
/// 文件转成字节
/// 并删除文件
/// 返回字节
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
public static byte[] GetFileStream(string filePath)
{
    var byteBuffer = File.ReadAllBytes(filePath);
    if (byteBuffer.Length > 0)
    {
        if (File.Exists(filePath))
        {
            File.Delete(filePath);
        }
        return byteBuffer;
    }
    return null;
}

对比之前针对数据写的代码,现在是完全抽离出来一个公共方法

页脚配置代码暂时也去除了,后续可能会添加进来

PDF配置项

/// <summary>
/// PDF配置项
/// </summary>
/// <typeparam name="T"></typeparam>
public class PDFConfig<T>
{
    /// <summary>
    /// 标题名称
    /// </summary>
    public string SiteName { get; set; }

    /// <summary>
    /// 任务名称
    /// </summary>
    public string TaskName { get; set; }

    /// <summary>
    /// 列名集合
    /// </summary>
    public List<string> ColumnNames { get; set; }

    /// <summary>
    /// 列宽集合
    /// </summary>
    public float[] ColumnWidths { get; set; }

    /// <summary>
    /// 数据集
    /// </summary>
    public List<T> DataList { get; set; }
}

模拟调用

PDFConfig<Student> studentList = new PDFConfig<Student>();
studentList.SiteName = "学生报表";
studentList.TaskName = string.Format("报表日期:{0}",DateTime.UtcNow.ToShortDateString());
// 正常情况从数据库取
studentList.DataList = new List<Student>();
studentList.ColumnNames = new List<string>
{
    "Id",
    "编号",
    "姓名",
    "等级",
    "专业",
    "地址",
};
studentList.ColumnWidths = new float[] { 50f, 120f, 60f, 150f, 95f, 95f };
byte[] file = PDFHelper.ExportPDF(studentList);
var stream = new MemoryStream(file);

通过配置项获取字节,然后通过字节生成文件流

最后Excel和PDF的文件流处理都大同小异

// PDF(pdf)
application/pdf
// Excel(xlsx)
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
// Word(docx)
application/vnd.openxmlformats-officedocument.wordprocessingml.document

被这风吹散的人说Ta爱的不深,被这雨淋湿的人说Ta不会冷