我创建了一个报告工具作为内部Web应用程序的一部分。该报告在GridView中显示所有结果,我使用JavaScript将GridView的内容逐行读入Excel对象。JavaScript继续在不同的工作表上创建数据透视表。
不幸的是,我没想到GridView的大小会导致浏览器过载问题,如果返回的时间超过几天。应用程序每天有几千条记录,假设每个月有60 k条记录,理想情况下我希望能够返回长达一年的所有结果。行数会导致浏览器挂起或崩溃。
我们在Visual Studio 2010上使用ASP.NET 3.5和SQL Server,预期的浏览器是IE8。该报告由一个网格视图组成,该视图根据用户选择的人群从少数存储过程中获取数据。网格视图位于UpdatePanel中:
<asp:UpdatePanel ID="update_ResultSet" runat="server">
<Triggers>
<asp:AsyncPostBackTrigger ControlID="btn_Submit" />
</Triggers>
<ContentTemplate>
<asp:Panel ID="pnl_ResultSet" runat="server" Visible="False">
<div runat="server" id="div_ResultSummary">
<p>This Summary Section is Automatically Completed from Code-Behind</p>
</div>
<asp:GridView ID="gv_Results" runat="server"
HeaderStyle-BackColor="LightSkyBlue"
AlternatingRowStyle-BackColor="LightCyan"
Width="100%">
</asp:GridView>
</div>
</asp:Panel>
</ContentTemplate>
</asp:UpdatePanel>
我对我的团队来说相对较新,所以我遵循了他们的典型做法,将sproc返回到DataTable,并将其用作后面代码中的DataSource:
List<USP_Report_AreaResult> areaResults = new List<USP_Report_AreaResult>();
areaResults = db.USP_Report_Area(ddl_Line.Text, ddl_Unit.Text, ddl_Status.Text, ddl_Type.Text, ddl_Subject.Text, minDate, maxDate).ToList();
dtResults = Common.LINQToDataTable(areaResults);
if (dtResults.Rows.Count > 0)
{
PopulateSummary(ref dtResults);
gv_Results.DataSource = dtResults;
gv_Results.DataBind();
(我知道你在想什么!但是是的,从那以后我学到了更多关于参数化的知识。)
LINQToDataTable函数没有什么特别的,只是将列表转换为数据表。
对于几千条记录(最多几天),这很好用。GridView显示结果,用户可以单击一个按钮来启动JScript导出器。外部JavaScript函数将每行读入Excel工作表,然后使用它创建数据透视表。数据透视表很重要!
function exportToExcel(sMyGridViewName, sTitleOfReport, sHiddenCols) {
//sMyGridViewName = the name of the grid view, supplied as a text
//sTitleOfReport = Will be used as the page header if the spreadsheet is printed
//sHiddenCols = The columns you want hidden when sent to Excel, separated by semicolon (i.e. 1;3;5).
// Supply an empty string if all columns are visible.
var oMyGridView = document.getElementById(sMyGridViewName);
//If no data is on the GridView, display alert.
if (oMyGridView == null)
alert('No data for report');
else {
var oHid = sHiddenCols.split(";"); //Contains an array of columns to hide, based on the sHiddenCols function parameter
var oExcel = new ActiveXObject("Excel.Application");
var oBook = oExcel.Workbooks.Add;
var oSheet = oBook.Worksheets(1);
var iRow = 0;
for (var y = 0; y < oMyGridView.rows.length; y++)
//Export all non-hidden rows of the HTML table to excel.
{
if (oMyGridView.rows[y].style.display == '') {
var iCol = 0;
for (var x = 0; x < oMyGridView.rows(y).cells.length; x++) {
var bHid = false;
for (iHidCol = 0; iHidCol < oHid.length; iHidCol++) {
if (oHid[iHidCol].length !=0 && oHid[iHidCol] == x) {
bHid = true;
break;
}
}
if (!bHid) {
oSheet.Cells(iRow + 1, iCol + 1) = oMyGridView.rows(y).cells(x).innerText;
iCol++;
}
}
iRow++;
}
}
**我想做的是:**创建一个解决方案(可能是客户端),可以处理这些数据并将其处理到Excel中。有人可能会建议使用HtmlTextWriter,但它不允许自动生成数据透视表,并创建一个令人讨厌的弹出警告。
我尝试过的:
- 填充一个JSON对象--我仍然认为这是有潜力的,但我还没有找到一种使它工作的方法。
- 使用SQLDataSource --我似乎无法使用它获取任何数据。
- 分页和循环浏览页面--混合的进度。虽然总体上很难看,但我仍然有一个问题,即为显示的每个页面查询并返回整个数据集。
**更新:**我仍然对替代解决方案持开放态度,但我一直在追求JSON理论。我有一个工作的服务器端方法,可以从DataTable生成JSON对象。我不知道如何将JSON传递到(外部)exportToExcel JavaScript函数....
protected static string ConstructReportJSON(ref DataTable dtResults)
{
StringBuilder sb = new StringBuilder();
sb.Append("var sJSON = [");
for (int r = 0; r < dtResults.Rows.Count; r++)
{
sb.Append("{");
for (int c = 0; c < dtResults.Columns.Count; c++)
{
sb.AppendFormat("\"{0}\":\"{1}\",", dtResults.Columns[c].ColumnName, dtResults.Rows[r][c].ToString());
}
sb.Remove(sb.Length - 1, 1); //Truncate the trailing comma
sb.Append("},");
}
sb.Remove(sb.Length - 1, 1);
sb.Append("];");
return sb.ToString();
}
有谁能展示一个例子,说明如何将这个JSON对象导入到外部JS函数中?或者任何其他导出到Excel的解决方案。
4条答案
按热度按时间mrfwxfqh1#
写CSV文件很简单也很高效。但是,如果您需要Excel,也可以以一种相当高效的方式完成,通过使用Microsoft Open XML SDK的开放XML Writer,可以处理60,000多行。
1.如果您还没有Microsoft Open SDK,请安装它(谷歌“下载Microsoft Open XML SDK”)
1.创建控制台应用程序
1.添加对DocumentFormat.OpenXml的引用
1.添加对WindowsBase的引用
1.尝试运行一些测试代码如下(将需要一些使用的)
请查看Vincent Tan在http://polymathprogrammer.com/2012/08/06/how-to-properly-use-openxmlwriter-to-write-large-excel-files/上的解决方案(下面,我稍微清理了他的示例以帮助新用户。
在我自己的使用中,我发现这对常规数据非常直接,但我确实不得不从我的真实的数据中删除“\0”字符。
...
如果你回顾一下这段代码,你会注意到两个主要的写入,首先是工作表,然后是包含工作表的工作簿。工作簿部分是最后的无聊部分,前面的工作表部分包含所有的行和列。
在你自己的改编中,你可以从你自己的数据中写入真实的的字符串值到单元格中。
值得注意的是,Excel中的行编号从1开始,而不是0。从索引0开始编号的行将导致“Corrupt file”错误消息。
最后,如果您正在处理非常大的数据集,永远不要调用ToList().使用数据读取器风格的方法流式传输数据。例如,您可以有一个IQueryable,并在每个的中使用它。您永远不会真的希望必须同时在内存中拥有所有数据,否则您将遇到内存不足和/或内存利用率过高的问题。
对于要流式传输的大文件,您可能需要尝试提供输出流,而不是文件名;然而,根据这个答案OpenXml and HttpResponse.OutputStream,OpenXML库需要能够从头开始查找,因此在托管文件之前保存到文件似乎是大量数据的唯一选择。
t5fffqht2#
我会尝试使用displaytag来显示结果。您可以设置它每页显示一定数量,这应该可以解决您的重载问题。然后,您可以设置displaytag以允许Excel导出。
z9zf31ra3#
我们通常使用一个“Export”命令按钮来处理这个问题,它连接到一个服务器端方法来获取数据集并将其转换为CSV。然后我们调整响应头,浏览器将其视为下载。我知道这是一个服务器端解决方案,但您可能需要考虑它,因为您将继续遇到超时和浏览器问题,直到您实现服务器端记录分页。
9fkzdhlc4#
自从我开始做这个问题以来,差不多一个半星期了,我终于在某种程度上让它全部工作起来了。我将暂时从标记答案开始,看看是否有人有更有效,更好的“最佳实践”方法。
通过生成JSON字符串,我已经将JavaScript与GridView分离。JSON是在数据填充时在代码后面生成的:
返回一个数据字符串,例如
[ {“来电者”:“John Doe”,“办公室”:“5555”,“类型”:“来电”,等等},
{“来电者”:“Jane Doe”,“办公室”:“7777”,“类型”:“传出”,等等},{等等} ]
我通过将文本分配给UpdatePanel中的Literal来隐藏此字符串:
JavaScript通过阅读div的内容来解析输出:
字符串输出和JavaScript 'eval'解析的速度都快得惊人,但是遍历JSON对象比我希望的要慢一些。
我相信这种方法将被限制在大约10亿个字符的数据--也许更少,这取决于内存测试的工作方式。(我已经计算过,我可能每天最多会看到100万个字符,所以在报告的一年内,这应该是可以的。)