C# 大数据导出word的假死报错的处理方法
发布时间:2022-09-25 07:19:48
最近一个项目是一个基于winform的报告系统,根据一系列的查询参数计算出结果,最终生成一个格式规范的word文档,刚开始数据量不大(500行)数据以内,写入速度还能接受,但最近遇到一个问题就是当出现大量的数据行的时候,写入word的过程就变的非常慢,CPU直接拉到100%,本人机器配置已经算比较高的了,8G内存+i5CPU,依旧假死,该问题困扰了我几天,也问了google很多次,基本上给出的答案都是word本身就比较慢这样一类的答案,或者是非托管代码的优化,慢也就算了,至少可以通过进度条来交互,假死到报错,这个绝对是零容忍的。尝试了很多种方法,包括将非托管代码强制进行回收,多线程等方式,最终都失败了,当然,黄天不负有心人最终总算是解决了问题了,我的数据量不算特别巨大,就4000多行写入表中,废话少说,直接贴代码:
常规写入word表格的方法:
private void LoadSectionWord()
{
//临时目录
string filePath = Tools.CreateWordFileDir() + Utils.GetTimeString() + ".doc";
//将二进制文件写入到word文件
sectionWordTools.ByteConvertWord(sectionWordTools.WordContentByTemplateIDAndTemplateTitle(templateId, sectionTitle), filePath);
try
{
//wdc为winWordControl控件的实例
wdC.LoadDocument(filePath);
Microsoft.Office.Interop.Word.Document wd = (Microsoft.Office.Interop.Word.Document)wdC.document;
Microsoft.Office.Interop.Word.Application wa = wd.Application;//
//需要写入Word的集合
if (dicList.Count > 0)
{
dicList = dicList.OrderBy(d => d.Key.AnalyteID).ToDictionary(i => i.Key, ii => ii.Value);
sectionWordTools.GotoBookMark(wa, "RepeatAnalysisResult");
wa.Selection.Copy();//复制模板第一个table
sectionWordTools.WordReplace("special matrix", wdg.Species.ToLower().Trim() + " " + wdg.Matrix.ToLower().Trim(), true, wd);
string analyteTitles = string.Empty;
int index = 0;
#region Replace Title
foreach (KeyValuePair<AnalyteReNameEntity, DataTable> d in dicList)
{
AnalyteReNameEntity key = d.Key;
if (dicList.Count > 2)
{
if (index.Equals(dicList.Count - 2))
{
analyteTitles += key.NewAnalyteName + " and ";
}
else
{
analyteTitles += key.NewAnalyteName + " , ";
}
}
else if (dicList.Count == 2)
{
analyteTitles += key.NewAnalyteName + " and ";
}
else
{
analyteTitles += key.NewAnalyteName;
}
index++;
}
analyteTitles = analyteTitles.Trim().TrimEnd('d').TrimEnd('n').TrimEnd('a').Trim().Trim(',');
sectionWordTools.WordReplace("for Abc000", "for " + analyteTitles, true, wd);
#endregion
int wordTableCount = 0;
foreach (KeyValuePair<AnalyteReNameEntity, DataTable> d in dicList)
{
AnalyteReNameEntity key = d.Key;
DataView dv = d.Value.DefaultView;
dv.Sort = "Custom ID";
DataTable dt = dv.ToTable();
#region 处理dt
if (dt.Columns["analyteid"] != null)
{
dt.Columns.Remove("analyteid");
} if (dt.Columns["id"] != null)
{
dt.Columns.Remove("id");
}
if (dt.Columns["reportid"] != null)
{
dt.Columns.Remove("reportid");
}
if (dt.Columns["studyid"] != null)
{
dt.Columns.Remove("studyid");
}
if (dt.Columns["analyteid"] != null)
{
dt.Columns.Remove("analyteid");
}
#endregion
//第一个WordTable
Microsoft.Office.Interop.Word.Table tb = wd.Tables[wd.Tables.Count];
#region 填充值
if (dt.Rows.Count > 0)
{
object beforerow = tb.Rows[2];
//表头
for (int i = 1; i <= dt.Columns.Count; i++)
{
tb.Cell(1, i).Range.Text = dt.Columns[i - 1].ColumnName;
}
for (int k = 1; k <= dt.Rows.Count; k++)
{
//模板上默认有2行了,添加行数
if (k <= dt.Rows.Count - 2)
{
tb.Rows.Add(ref beforerow);
}
for (int i = 1; i <= dt.Columns.Count; i++)
{
tb.Cell(k + 1, i).Range.Text = dt.Rows[k - 1][dt.Columns[i - 1].ColumnName].ToString();
}
}
}
#endregion
sectionWordTools.WordReplace("Abc000", key.NewAnalyteName, true, wd);
#region 处理备注
string notStr = GetCurrentReassayReason(key.AnalyteID);
//notStr = "Reasons for Reassay:\r1). Confirmation Assay\r2). ALQ\rReasons for Reported Conc.:\r1). The only valid result is reported.\r20. Reassay results selected according to procedure defined in study protocol";
sectionWordTools.WordReplace("Repeat analysis Tipis", notStr, true, wd);
#endregion
//根据内容调整表格
//tb.AutoFitBehavior(Microsoft.Office.Interop.Word.WdAutoFitBehavior.wdAutoFitContent);
//根据窗口调整表格
//tb.AutoFitBehavior(Microsoft.Office.Interop.Word.WdAutoFitBehavior.wdAutoFitWindow);
if (wordTableCount < dicList.Count - 1)
{
// wa.Selection.TypeParagraph();//回车
wd.Paragraphs.Last.Range.Select();
wa.Selection.Paste();
}
wordTableCount++;
#region 处理整体格式
object lefttopstr = tb.Cell(2, 1).Range.Start;
object leftbottomend = tb.Cell(tb.Rows.Count, tb.Columns.Count).Range.End;
wd.Range(ref lefttopstr, ref leftbottomend).Select();
sectionWordTools.AddBorderNoneLineStyle(wa, false, false, true, true, true, true, true, true);
#endregion
}
}
}
catch (Exception ex)
{
Tools.RecordErrorList(ex.Message, ex);
}
}
上面的代码就是通过winwordControl控件加载word模板,一行一行向word表格中写入数据,新的表格通过书签复制后粘贴,然后继续一行一行的写入,数据量在500行以内的时候,速度还是可以接受的,当数据量增大之后,就很慢了
改进后的代码,主要用到了分页的思想,将集合或者datatable进行分页,每页处理50行数据,处理完了之后保存到临时文件,释放掉word资源,再打开临时文件,接着上次的写入位置继续写入。释放word资源就是为了解决写入数据量过大假死报错的问题。希望对处理word的朋友有帮助
private void LoadSectionWord()
{
string filePath = Tools.CreateWordFileDir() + Utils.GetTimeString() + ".doc";
//读取模板
sectionWordTools.ByteConvertWord(sectionWordTools.WordContentByTemplateIDAndTemplateTitle(templateId, sectionTitle), filePath);
try
{
wdC.LoadDocument(filePath);
Microsoft.Office.Interop.Word.Document wd = (Microsoft.Office.Interop.Word.Document)wdC.document;
Microsoft.Office.Interop.Word.Application wa = wd.Application;
sectionWordTools.GotoBookMark(wa, "TimeConcentrationData");
wa.Selection.Copy();//复制模板第一个table
string assayLLOQ = ComputerOfPostText.ComputerPostText.GetStudyAssayLLOQResults(this.studyId);
sectionWordTools.WordReplace("0.0 ng/mL for Abc000", assayLLOQ, true, wd);
//获得分组,只有分组了的才统计,默认已经分好组,组可以调整
List<string> groupList = commonBLL.GetAnalyteGroupList(this.reportId, COMMON_TYPE);
List<List<AnalyteReNameEntity>> analyteGroupList = new List<List<AnalyteReNameEntity>>();
#region 确定Table分组的数量
foreach (string group in groupList)
{
List<AnalyteReNameEntity> currentGroupList = commonBLL.GetAnalyteReNameList(this.reportId, this.studyId, group);
if (currentGroupList.Count > 0)
{
analyteGroupList.Add(currentGroupList);
}
}
#endregion
if (analyteGroupList.Count > 0)
{
int wordTableCount = 0;
foreach (List<AnalyteReNameEntity> currentGroupList in analyteGroupList)
{
//第一个WordTable
Microsoft.Office.Interop.Word.Table tb = wd.Tables[wd.Tables.Count];
string key = globalBLL.AnalyteAppendString(currentGroupList);
//需要写入word的结果集
DataTable dt = new DataTable();
#region 构造word中的列头
foreach (ReportColumnsEntity rc in rceList)
{
dt.Columns.Add(rc.ReportText, typeof(string));
}
foreach (AnalyteReNameEntity ar in currentGroupList)
{
dt.Columns.Add(ar.NewAnalyteName + " Concentration (ng/mL)", typeof(string));
}
dt.Columns.Add("Comments", typeof(string));
#endregion
#region 构造Word中的行的DataTable
for (int i = 0; i < currentGroupList.Count; i++)
{
DataRow[] rows = dtResult.Select(string.Format(" analyteid={0}", currentGroupList[i].AnalyteID));
if (i == 0)
{
#region i=0,填充包括列头
for (int k = 0; k < rows.Length; k++)
{
string conc = Utils.EffectiveNumber(rows[k]["concentration"].ToString(), "3");
DataRow dr = dt.NewRow();
foreach (ReportColumnsEntity rc in rceList)
{
if (rc.WatsonText.Equals("concentration"))
{
dr[rc.ReportText] = Utils.EffectiveNumber(rows[k][rc.WatsonText].ToString(), "3");
}
else
{
dr[rc.ReportText] = rows[k][rc.WatsonText].ToString();
}
}
dr["Comments"] = "NA";
dr[currentGroupList[i].NewAnalyteName + " Concentration (ng/mL)"] = conc;
dt.Rows.Add(dr);
}
#endregion
}
else
{
for (int k = 0; k < rows.Length; k++)
{
string conc = Utils.EffectiveNumber(rows[k]["concentration"].ToString(), "3");
//对分析物浓度列重新赋值
dt.Rows[k][currentGroupList[i].NewAnalyteName + " Concentration (ng/mL)"] = conc;
}
}
}
DataTable dtTemp = dt.Copy();
DataView dv = dt.DefaultView;
dv.Sort = "Subject ID";
dtTemp = dv.ToTable();
dt = dtTemp;
#endregion
#region 填充值
if (dt.Rows.Count > 0)
{
#region 根据列合并拆分单元格
object wordColumnsCount = dt.Columns.Count;
object rownum = 1;
for (int k = 0; k < 5; k++)
{
for (int i = 1; i < tb.Columns.Count; i++)
{
tb.Cell(k, i).Range.Text = " ";
}
}
//先合并,再拆分 Selection.Cells.Merge
for (int i = 1; i <= 4; i++)
{
object start = tb.Cell(i, 1).Range.Start;
object end = tb.Cell(i, 6).Range.End;
wd.Range(ref start, ref end).Select();
wa.Selection.Cells.Merge();
}
for (int i = 1; i < 5; i++)
{
tb.Cell(i, 1).Split(ref rownum, ref wordColumnsCount);
}
#endregion
//关闭屏幕更新
wa.ScreenUpdating = false;
#region 表头的填充
//表头填充
for (int i = 1; i <= dt.Columns.Count; i++)
{
tb.Cell(1, i).Range.Text = dt.Columns[i - 1].ColumnName;
}
#endregion
#region 填充值
#region 分页
object missing = System.Reflection.Missing.Value;
object saveOption = Microsoft.Office.Interop.Word.WdSaveOptions.wdDoNotSaveChanges; //解决normal.dot问题
object beforerow = tb.Rows[3];
int dtRowCounts = dt.Rows.Count;//10;//
int columnCount = dt.Columns.Count;
int pageSize = 50;
//分页的总页数
int totalPages = DataUtils.GetPageCounts(dtRowCounts, pageSize);
for (int index = 1; index <= totalPages; index++)
{
tb = wd.Tables[wd.Tables.Count];
beforerow = tb.Rows[3 + (index - 1) * pageSize];
DataTable pageDt = DataUtils.GetPagedTable(dt, index, pageSize);
#region 添加行和数据
for (int k = 2; k < pageDt.Rows.Count + 2; k++)
{
//模板上已经有三行了,最后一页少添加3行
if (index.Equals(totalPages))
{
if (k < pageDt.Rows.Count - 3)
{
tb.Rows.Add(ref beforerow);
}
}
else
{
tb.Rows.Add(ref beforerow);
}
//添加行的同时填充单元格的值,减少一次全循环
for (int i = 1; i <= pageDt.Columns.Count; i++)
{
tb.Cell(k + (index - 1) * pageSize, i).Range.Text = pageDt.Rows[k - 2][pageDt.Columns[i - 1].ColumnName].ToString();
}
}
#endregion
//填充完PageSize条数据,先保存再重新加载填充
object savePath = filePath;
wd.SaveAs(ref savePath, ref missing,
ref missing, ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing);
wa.ScreenUpdating = true;
wd = null;
wa = null;
Thread.Sleep(10);
//重新加载
wdC.LoadDocument(filePath);
wd = (Microsoft.Office.Interop.Word.Document)wdC.document;
wa = wd.Application;
wa.ScreenUpdating = false;
}
#endregion
#endregion
//打开屏幕更新
//wa.ActiveDocument.ActiveWindow.WindowState = Microsoft.Office.Interop.Word.WdWindowState.wdWindowStateMaximize;
wa.ScreenUpdating = true;
}
#endregion
#region Paste Table
sectionWordTools.WordReplace("Abc000", key, true, wd);
tb = wd.Tables[wd.Tables.Count];
//根据内容调整表格
tb.AutoFitBehavior(Microsoft.Office.Interop.Word.WdAutoFitBehavior.wdAutoFitContent);
//根据窗口调整表格
tb.AutoFitBehavior(Microsoft.Office.Interop.Word.WdAutoFitBehavior.wdAutoFitWindow);
if (wordTableCount < analyteGroupList.Count - 1)
{
// wa.Selection.TypeParagraph();//回车
wd.Paragraphs.Last.Range.Select();
wa.Selection.Paste();
}
wordTableCount++;
#endregion
}
}
}
catch (Exception ex)
{
Tools.RecordErrorList(ex.Message, ex);
}
}


猜你喜欢
- import java.io.File;import java.util.ArrayList;public class FileTest {
- 在 Android 加载图片一般使用 ImageView,这里简单记录一下这个控件的使用方法。最简单就是在 xml 里直接使用 ImageV
- 需求:用TreeSet集合存储多个学生信息(姓名,语文成绩,数学成绩),并遍历该集合;要按照总分从高到低进行排序分析:1.创建学生类 成员变
- 本文是项目中使用了websocket进行一些数据的推送,对比项目做了一个demo,ws的相关问题不做细数,仅做一下记录。此demo针对ws的
- substring(参数)是java截取字符串的一个方法。它有两种传参的方式:第一种:public String substring(int
- 一、查看线程的运行状态题目线程有以下6种状态:新建、运行、阻塞、等待、计时等待和终止。new新线程时,线程处于新建 状态。调用start()
- 本文实例讲述了Java面向接口编程之命令模式。分享给大家供大家参考,具体如下:一 点睛某个方法需要完成某个行为,但这个行为的具体实现无法确定
- @Transactional跟@DS动态数据源注解冲突背景前阵子写一个项目时,有个需求是要往3个库,3个表里插入数据,在同一个方法里,公司是
- 我这里有一个需求需要修改Person类中的一个属性上的注解的值进行修改,例如:public class Person { private i
- 本文实例讲述了java实现List中对象排序的方法。分享给大家供大家参考,具体如下:package com.test; import jav
- 本文实例为大家分享了C#实现简易多人聊天室的具体代码,供大家参考,具体内容如下只有一个群聊的功能服务端using System;using
- 我们再用支付宝支付的时候,会从底部弹上来一个对话框,让我们选择支付方式等等,今天我们就来慢慢实现这个功能效果图实现主界面很简单,就是一个按钮
- 效果就是这样,RecyclerView列表可拖拽排序,可删除,可添加;RecyclerView给我们提供了一个手势器:ItemTouchHe
- 最近没事写了一个简易浏览器,在刚开始写的时候遇到一些问题,主要的问题就是如何在自己的webview中显示所有的网页数据,不过不指
- MyBatis简介MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架。 MyBatis 消除了几乎所有的 JDBC
- 一、显示信息对话框:使用“JOptionPane.showMessageDialog”显示:图标对话框类型语法显示错误类型对话框showMe
- 回调函数就像activities一样,fragments也有它们自己的生命周期。理解fragments的生命周期,可以使你在它们被销毁的时候
- <?xml version="1.0" encoding="UTF-8" ?> <
- 这篇博客主要介绍的是 Android 主流各种机型和各种版本的悬浮窗权限适配,但是由于碎片化的问题,所以在适配方面也无法做到完全的主流机型适
- 一、Stream流介绍在JDK8时,JAVA新增了lambda表达式,它与 java.io 包里的 InputStream和 OutputS