C#中word导出功能骚操作

码农公社  210.net.cn   210是何含义?10月24日是程序员节,1024 = 210、210既 210 之意。

功能需求:企业填报自己的企业信息到系统中,最后需要将数据以word模板形式导出,功能简单,就是要开发快,赶及

需求分析:主要费时间的工作是设计表单,以及导出数据,因为表单较多,一个表单可能有100多个字段,一个一个对,头发也得成把的掉!

解决方案:在导出word功能模块儿,写一些通用的方法,减少一些工作量。

                 word数据导出功能,就是在word模板中每一个需要填数据的地方命名一个标签,代码中找到对应命名的标签,插入数值。

传统做法:

第一步:在word模板中填写标签  

第二步:程序中每个插入字段数据和word模板标签对应,最后插值,这样做 有一个问题就是比较耗时间,而且很容易出错

我的做法:

给数据字段一个自定义特性,在自定定义特性中写上对应的标签地址,应用反射的方法将数据最终插入到word模板中。这样就省去了在word中写标签这样繁杂的操作。这样做的问题就是性能比较差,但是可以忽略不计。

开写:

新建一个项目:ExportWordModel

1.jpg

1.jpg

最终项目简易结构:

1.jpg

将没用的东西全部去掉,修改Index.cshtml页面成这样:

@{
    ViewBag.Title = "Home Page";
}
<div class="jumbotron" style="text-align: center">
    @*<h1>ASP.NET</h1>*@
    <input type="button" value="导出" onclick="location.href = '@Url.Action("GetExport","Home")'" />
</div>

在 HomeController 中创建:GetExport

创建一个类ExportFileOperator(所有的word操作),此类需要继承Controller,因为有返回File操作方法

1.jpg

1、 在GetExport中首先命名一个导出word标题就叫:测试导出Word文件

string title = "测试导出Word文件";
创建doc:

 var doc = ExportFileOperator.CreateBuilder("GroupForm.doc");

2、CreateBuilder方法实现为(此处操作需要Aspose.Word组件,是操作word的,这个需要大家自己去找一下,或者网上找个破解的):

private static string _tempPath = AppDomain.CurrentDomain.BaseDirectory;
public static (Document doc, DocumentBuilder builder) CreateBuilder(string tempFileName)
{
   string tempPath = $"{_tempPath}{tempFileName}";
   Document doc = new Document(tempPath);
   return (doc, new DocumentBuilder(doc));
}

3、插入标题(需要在word中写一个标签,作为标题插入的地址):

1.jpg

最终可以显示结果为这样:

1.jpg

方法:

ExportFileOperator.InsertTitle(ref doc.Item2, title);//插入标题 
public static void InsertTitle(ref DocumentBuilder builder, string fileName, string tempBookMarkName = "title")
{
     builder.MoveToBookmark(tempBookMarkName);
     builder.Write(fileName);
}

 4、根据业务实体,将实体数据写入到word中,也是核心所在

首先命名一个数据类:

public class EnterpriseEntity
    {
         #region 实体成员
        /// <summary>
        /// id
        /// </summary>
        public string id { get; set; }
        /// <summary>
        /// 团队名
        /// </summary>
        [Description("企业名称")]
        public string v1 { get; set; }
        /// <summary>
        /// 统一社会信用代码
        /// </summary>
        [Description("统一社会信用代码")]
        public string v3 { get; set; }
        /// <summary>
        /// 成立日期
        /// </summary>
        [Description("成立日期")]
        public string v4 { get; set; }

        /// <summary>
        /// 参赛行业领域
        /// </summary>
        [Description("参赛行业领域")]
        public string v5 { get; set; }
        /// <summary>
        /// 行政区域
        /// </summary>
        [Description("行政区域")]
        public string v6 { get; set; }
        /// <summary>
        /// 是否属于国家高新区内的企业
        /// </summary>
        [Description("属于国家高新区内的企业")]
        public string v7 { get; set; }
        /// <summary>
        /// 是否属于国家级经济开发区内的企业
        /// </summary>
        [Description("属于国家级经济开发区内的企业")]
        public string v9 { get; set; }
        /// <summary>
        /// 是否属于国家级科技企业孵化器内的企业
        /// </summary>
        [Description("属于国家级科技企业孵化器内的企业")]
        public string v11 { get; set; }
        /// <summary>
        /// 是否属于国家大学科技园内的企业
        /// </summary>
        [Description("属于国家大学")]
        public string v13 { get; set; }
        /// <summary>
        /// 是否国家备案的众创空间内的企业
        /// </summary>
        [Description("国家备案的众创空间内的企业")]
        public string v20 { get; set; }
        /// <summary>
        /// 企业注册类型
        /// </summary>
        [Description("企业注册类型")]
        public string v22 { get; set; }
        /// <summary>
        /// 注册资本
        /// </summary>
        [Description("注册资本")]
        public string v24 { get; set; }
        /// <summary>
        /// 实收资本(万元人民币)
        /// </summary>
        [Description("实收资本")]
        public string v25 { get; set; }
        /// <summary>
        /// 企业注册地址
        /// </summary>
         [Description("企业注册地址")]
        public string v26 { get; set; }
        /// <summary>
        /// 邮政编码
        /// </summary>
        [Description("注册地邮政编码")]
        public string v27 { get; set; }
        /// <summary>
        /// 通信地址
        /// </summary>
          [Description("通信地址")]
        public string v28 { get; set; }
        /// <summary>
        /// 邮政编码
        /// </summary>
         [Description("通讯地邮政编码")]
        public string v29 { get; set; }
        /// <summary>
        /// 企业网址
        /// </summary>
        [Description("企业网址")]
        public string v30 { get; set; }
        /// <summary>
        /// 企业官方微信
        /// </summary>
        [Description("企业官方微信")]
        public string v31 { get; set; }
        /// <summary>
        /// 职工总数
        /// </summary>
         [Description("职工总数")]
        public string v32 { get; set; }
        /// <summary>
        /// 博   士人数
        /// </summary>
        [Description("博   士")]
        public string v33 { get; set; }
        /// <summary>
        /// 硕   士人数
        /// </summary>
        [Description("硕   士")]
        public string v34 { get; set; }
        /// <summary>
        /// 本   科人数
        /// </summary>
        [Description("本   科")]
        public string v35 { get; set; }
        /// <summary>
        /// 大专及以下人数
        /// </summary>
        [Description("大专及以下")]
        public string v36 { get; set; }
        /// <summary>
        /// 高级职称人数
        /// </summary>
        [Description("高级职称")]
        public string v37 { get; set; }
        /// <summary>
        /// 中级职称人数
        /// </summary>
        [Description("中级职称")]
        public string v38 { get; set; }
        /// <summary>
        /// 初级职称人数
        /// </summary>
        [Description("初级职称")]
        public string v39 { get; set; }
        /// <summary>
        /// 高级技工人数
        /// </summary>
        [Description("高级技工")]
        public string v40 { get; set; }
        /// <summary>
        /// 上市公司控股企业是否
        /// </summary>
        [Description("上市公司控股企业")]
        public string v41 { get; set; }
        /// <summary>
        /// 新三板企业是否
        /// </summary>
        [Description("新三板企业")]
        public string v42 { get; set; }
        /// <summary>
        /// 高新技术企业是否
        /// </summary>
        [Description("高新技术企业")]
        public string v43 { get; set; }
        /// <summary>
        /// 获得时间
        /// </summary>
        [Description("获得时间")]
        public string v44 { get; set; }
        /// <summary>
        /// 登记入库的科技型中小企业是否
        /// </summary>
        [Description("登记入库的科技型中小企业")]
        public string v45 { get; set; }
        /// <summary>
        /// 企业概要(不超1000字)
        /// </summary>
        [Description("企业概要")]
        public string v46 { get; set; }
        /// <summary>
        /// 关 键 词
        /// </summary>
        [Description("关 键 词")]
        public string v47 { get; set; }
        /// <summary>
        /// 现融资阶段
        /// </summary>
        [Description("现融资阶段")]
        public string v48 { get; set; }
        /// <summary>
        /// 参赛项目产品图片
        /// </summary>
        public string v49 { get; set; }
        /// <summary>
        /// 参赛项目收入占去年企业营业收入比例
        /// </summary>
        [Description("参赛项目收入占去年企业营业收入比例")]
        public string v50 { get; set; }
        /// <summary>
        /// 参赛项目介绍(1000字以内)
        /// </summary>
        [Description("参赛项目介绍(1000字以内)")]
        public string v51 { get; set; }
        /// <summary>
        /// 当前五大客户
        /// </summary>
        [Description("当前五大客户")]
        public string v52 { get; set; }
        /// <summary>
        /// 当前五大供应商
        /// </summary>
        [Description("当前五大供应商")]
        public string v53 { get; set; }
        /// <summary>
        /// 国内市场地位排名
        /// </summary>
        [Description("国内市场地位排名")]
        public string v54 { get; set; }
        /// <summary>
        /// 商业模式及业务拓展计划
        /// </summary>
        [Description("商业模式及业务拓展计划")]
        public string v56 { get; set; }
        /// <summary>
        /// 经营风险与对策
        /// </summary>
        [Description("经营风险与对策")]
        public string v57 { get; set; }
        /// <summary>
        /// 企业管理模式
        /// </summary>
        [Description("企业管理模式")]
        public string v58 { get; set; }
        /// <summary>
        /// 公司对管理层及关键人员是否已采取激励措施是否
        /// </summary>
        [Description("公司对管理层及关键人员是否已采取激励措施")]
        public string v59 { get; set; }
        /// <summary>
        /// 公司是否考虑员工持股问题?是否
        /// </summary>
        [Description("公司是否考虑员工持股问题")]
        public string v60 { get; set; }
        /// <summary>
        /// 企业其他技术、产品及服务(1000字以内)
        /// </summary>
        [Description("企业其他技术、产品及服务(1000字以内)")]
        public string v61 { get; set; }
        /// <summary>
        /// 参赛目的
        /// </summary>
        [Description("参赛目的")]
        public string v62 { get; set; }
        /// <summary>
        /// 并购需求
        /// </summary>
        [Description("并购需求")]
        public string v63 { get; set; }
        /// <summary>
        /// 申请大赛组织的大企业对接活动是否
        /// </summary>
        [Description("申请大赛组织的大企业对接活动")]
        public string v64 { get; set; }
        /// <summary>
        /// 资金使用计划
        /// </summary>
        [Description("债权融资资金使用计划")]
        public string v65 { get; set; }
        /// <summary>
        /// 股权融资需求是否  
        /// </summary>
        [Description("直接从事研发科技人员数")]
        public string v66 { get; set; }
        /// <summary>
        /// 融资金额(万元¥)
        /// </summary>
        [Description("上年度吸纳高校应届毕业生人数")]
        public string v67 { get; set; }
        /// <summary>
        /// 拟出让股权比例
        /// </summary>
        [Description("参赛项目名称")]
        public string v68 { get; set; }
        /// <summary>
        /// 融资时间
        /// </summary>
        [Description("产品市场分析及竞争优势")]
        public string v69 { get; set; }
        /// <summary>
        /// 资金使用计划
        /// </summary>
        [Description("股权融资资金使用计划")]
        public string v70 { get; set; }
        /// <summary>
        /// 申请大赛推荐投资机构是否  (修改 申请大赛推荐信贷机构)
        /// </summary>
        [Description("申请大赛推荐信贷机构")]
        public string v71 { get; set; }
        /// <summary>
        /// 申请大赛组织的融资路演是否 (修改 申请大赛推荐投资机构)
        /// </summary>
        [Description("申请大赛推荐投资机构")]
        public string v72 { get; set; }
        /// <summary>
        /// 申请国家科技成果转化引导基金设立的子基金推荐 (修改 申请大赛组织的融资路演)
        /// </summary>
        [Description("申请大赛组织的融资路演")]
        public string v73 { get; set; }
        #endregion

        public List<string> GetThisDescriptionName()
        {
            var result = new List<string>();
            GetType().GetProperties().ToList().ForEach(f =>
           {
               var descriptionObj = (DescriptionAttribute[])f.GetCustomAttributes(typeof(DescriptionAttribute), false);
               if (descriptionObj.Length > 0 && !string.IsNullOrWhiteSpace(descriptionObj[0].Description))
               {
                   result.Add(descriptionObj[0].Description);
               }
           });
            return result;
        }
    }

其中重要的地方是:需要给每个字段一个Description,这里面的值对应的就是word模板中的名称,如下:

1.jpg

这里因为数据是保密的,我就将一些字段删除了,包括word模板中的一些也删除了,就拿出一部分。

和数据库交互的部分我也没写,就将查出来的数据先命名一个_enterpriseStr,最后用Newtonsoft转换成实体这样操作了哈:

 EnterpriseEntity enterprise = JsonConvert.DeserializeObject<EnterpriseEntity>(_enterpriseStr); 

1.jpg

5、将查出来的数据,插入到word中,完成最终的导出:

ExportFileOperator.InsertFormData(enterprise, ref doc.Item1);//实体数据插入
return new ExportFileOperator().FileResult(title, doc.Item1);

其中最重要的方法就是InsertFormData这个,他的实现如下:

public static void InsertFormData<T>(T objFormData, ref Document document)
{
            NodeCollection allTables = document.GetChildNodes(NodeType.Table, true);
            List<string> headDescribeNameList = GetObjectHeadDescription<T>();//获取实体中每个Description中的值
            foreach (Table tableFirst in allTables)
            {
                for (int headIndex = 0; headIndex < headDescribeNameList.Count; headIndex++)//循环实体中的每个DescribeName
                {
                    for (int rowIndex = 0; rowIndex < tableFirst.Rows.Count; rowIndex++)//遍历word模板中所有的table
                    {
                        for (int cellIndex = 0; cellIndex < tableFirst.Rows[rowIndex].Cells.Count; cellIndex++)//遍历模板中所有的table每行的列数
                        {
                            if (tableFirst.Rows[rowIndex].Cells[cellIndex].GetText() != null && tableFirst.Rows[rowIndex].Cells[cellIndex].GetText().Contains(headDescribeNameList[headIndex]) &&
                                  ((tableFirst.Rows[rowIndex].Cells.Count > cellIndex && tableFirst.Rows[rowIndex].Cells[cellIndex + 1] != null && tableFirst.Rows[rowIndex].Cells[cellIndex + 1].GetText().Equals("a")) || (tableFirst.Rows.Count > rowIndex && tableFirst.Rows[rowIndex + 1] != null && tableFirst.Rows[rowIndex + 1].Cells[cellIndex] != null && tableFirst.Rows[rowIndex + 1].Cells[cellIndex].GetText().Equals("a"))))//如果遍历的cell不为空、其中的值能和DescribeName匹配上,并且这个单元的右边的cell或者下边cell有占位,而且是空,就在此处插入值
                            {
                                var objValue = GetObjectValueByPropName(objFormData, headDescribeNameList[headIndex]);//根据DescribeName获取对应的值
                                if (tableFirst.Rows[rowIndex].Cells.Count > cellIndex && tableFirst.Rows[rowIndex].Cells[cellIndex + 1] != null && tableFirst.Rows[rowIndex].Cells[cellIndex + 1].GetText().Equals("a"))
                                {
                                    InsertCell(objValue, document, tableFirst.Rows[rowIndex].Cells[cellIndex + 1]);//优先在右变空位插入值
                                    break;
                                }
                                InsertCell(objValue, document, tableFirst.Rows[rowIndex + 1].Cells[cellIndex]);//右侧如果没有就在下边空位插入值
                                break;
                            }
                        }
                    }
                }
            }
 }

 public static List<string> GetObjectHeadDescription<T>()
 {
    var obj = Activator.CreateInstance<T>();
    MethodInfo method = obj.GetType().GetMethod("GetThisDescriptionName", new Type[] { });//每个实体需要有GetThisDescriptionName这个方法
    return (List<string>)(method?.Invoke(obj, null));
  }
其中GetThisDescriptionName方法需求在每个实体类中有实现:
https://img2020.cnblogs.com/blog/969477/202102/969477-20210210102003562-1369498077.png
根据descriptionName获取实体中的值:
private static string GetObjectValueByPropName<T>(T objFormData, string descriptionName)
{
            try
            {
                var properties = objFormData.GetType().GetProperties();
                foreach (var propertyInfo in properties)
                {
                    var descriptionAttributes = (DescriptionAttribute[])propertyInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
                    if (descriptionAttributes.Length > 0 && !string.IsNullOrWhiteSpace(descriptionAttributes[0].Description) && descriptionAttributes[0].Description.Equals(descriptionName))
                    {
                        return propertyInfo.GetValue(objFormData) == null ? "无" : propertyInfo.GetValue(objFormData).ToString();
                    }
                }
                return "无";
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
}

在cell中插入值:

private static void InsertCell(string value, Document doc, Cell cell)
 {
            Cell insertCell = cell;
            insertCell.FirstParagraph.Remove();
            Paragraph p = new Paragraph(doc);
            p.AppendChild(new Run(doc, (value == null ? "" : value)));
            p.ParagraphFormat.Alignment = ParagraphAlignment.Center;
            insertCell.CellFormat.VerticalAlignment = CellVerticalAlignment.Center;
            insertCell.AppendChild(p);
 }

最后一个方法FileResult:

public FileResult FileResult(string fileName, Document doc)
{
            var filePathName = $"{fileName}.doc";
            doc.Save(Path.Combine(_tempPath, "temp", filePathName), SaveFormat.Doc); //保存word
            filePathName = Path.Combine(_tempPath, "temp", filePathName);
            return File(filePathName, "application/doc", $"{fileName}.Doc");
}

最终效果:

1.jpg

1.jpg


评论