- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我的问题是我需要将 90.000+ 行/143 列从 DataGridView(从 MySQL 数据库填充)导出到 Excel。无论我做什么,我总是在 45k-60k 行之后出现“System.Out.Of.Memory”异常,具体取决于解决方案。我知道可能会有诸如“为什么需要这么多行”之类的问题,我会回答“不幸的是,这是需要的”。我搜索了有关我的问题的论坛,但没有找到任何可行的解决方案。我尝试将 StreamWriter 转换为 CSV,分 block 处理数据(下面的解决方案),还使用多个 Excel 或 CSV 文件,但没有任何帮助。每次执行期间,当我尝试使用较少的行数时,RAM 使用量都会增长,并且在成功导出后不会释放。我不知道何时以及是否在成功执行后释放 RAM。
测试机器有 8 GB 的 RAM 并且使用的是 Windows 10。不幸的是,我无法使用 MySQL 服务器的资源在那里处理 Excel 导出,然后输出文件与用户共享,所以我需要使用客户端机器。
下面是我最新的不起作用的解决方案,其中数据从 DGV 读取并以 block 的形式写入 Excel。更改 block 的大小不会减少内存消耗,如果我将其缩小(如 500 到 2000),唯一的影响是导出速度越来越慢。
Imports Excel = Microsoft.Office.Interop.Excel
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
If DataGridView1.Rows.Count > 0 Then
Dim filename As String = ""
Dim SV As SaveFileDialog = New SaveFileDialog()
SV.FileName = "Worst_cells"
SV.Filter = "xlsx files (*.xlsx)|*.xlsx|All files (*.*)|*.*"
SV.FilterIndex = 1
SV.RestoreDirectory = True
Dim result As DialogResult = SV.ShowDialog()
If result = DialogResult.OK Then
filename = SV.FileName
Dim XCELAPP As Microsoft.Office.Interop.Excel.Application = Nothing
Dim XWORKBOOK As Microsoft.Office.Interop.Excel.Workbook = Nothing
Dim XSHEET As Microsoft.Office.Interop.Excel.Worksheet = Nothing
Dim misValue As Object = System.Reflection.Missing.Value
XCELAPP = New Excel.Application()
XWORKBOOK = XCELAPP.Workbooks.Add(misValue)
XCELAPP.DisplayAlerts = False
XCELAPP.Visible = False
XSHEET = XWORKBOOK.ActiveSheet
XSHEET.Range("B1").ColumnWidth = 11
For Each column As DataGridViewColumn In DataGridView1.Columns
XSHEET.Cells(1, column.Index + 1) = column.HeaderText
Next
Dim rowCnt As Integer = DataGridView1.Rows.Count
Dim colCnt As Integer = DataGridView1.Columns.Count
Dim batchSize As Integer = 10000
Dim currentRow As Integer = 0
Dim valueObjArray As Object(,) = New Object(batchSize - 1, colCnt - 1) {}
While currentRow < rowCnt
Dim rowIndex As Integer = 0
While rowIndex < batchSize AndAlso currentRow + rowIndex < rowCnt
For colIndex As Integer = 0 To colCnt - 1
valueObjArray(rowIndex, colIndex) = DataGridView1(colIndex, currentRow + rowIndex).Value
Next
rowIndex += 1
End While
Dim colName As String = ColumnLetter(colCnt)
If (currentRow + batchSize + 1) < rowCnt Then
XSHEET.Range("A" + (currentRow + 2).ToString(), colName + (currentRow + batchSize + 1).ToString()).Value2 = valueObjArray
Else
XSHEET.Range("A" + (currentRow + 2).ToString(), colName + (rowCnt + 1).ToString()).Value2 = valueObjArray
End If
XWORKBOOK.SaveAs(filename)
currentRow += batchSize
End While
XCELAPP.DisplayAlerts = True
XWORKBOOK.Close(False)
XCELAPP.Quit()
Try
System.Runtime.InteropServices.Marshal.ReleaseComObject(XSHEET)
System.Runtime.InteropServices.Marshal.ReleaseComObject(XWORKBOOK)
System.Runtime.InteropServices.Marshal.ReleaseComObject(XCELAPP)
Catch
End Try
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
GC.WaitForPendingFinalizers()
End If
End If
End Sub
最佳答案
确认使用Marshal.ReleaseComObject(...);
在 Range
对象修复 OutOfMemory
异常(exception)。下面是用于测试的代码。您将不得不用自己的代码替换几行代码。代码的第一部分是生成大量随机数据。第二部分写出DataTable
成 block 的行。通过设置 xls.Visible = true;
您可以通过 Excel 窗口底部的进度条看到 Excel 处理每个 block 。
public static void TestExcel(String filename, int maxRows) {
int numCols = 100;
Type[] availTypes = new Type[] { typeof(bool), typeof(int), typeof(double), typeof(String), typeof(DateTime) };
Type[] types = new Type[numCols];
Random r = new Random();
DataTable table = new DataTable();
for (int i = 0; i < numCols; i++) {
Type ty = availTypes[r.Next(availTypes.Length)];
types[i] = ty;
table.Columns.Add("Col" + i, ty);
}
DateTime minDate = new DateTime(1901,01,01);
for (int i = 0; i < maxRows; i++) {
Object[] arr2 = new Object[numCols];
for (int j = 0; j < numCols; j++) {
Object o = null;
Type ty = types[j];
if (ty == typeof(bool))
o = (r.Next(2) == 0 ? false : true);
else if (ty == typeof(int))
o = r.Next(int.MinValue, int.MaxValue);
else if (ty == typeof(double))
o = r.NextDouble();
else if (ty == typeof(String)) {
int len = r.Next(0, 256);
char c = ExcelUtils.ToLetters(r.Next(26))[0];
o = new String(c, len);
}
else if (ty == typeof(DateTime))
o = minDate.AddSeconds(r.Next(int.MaxValue));
arr2[j] = o;
}
table.Rows.Add(arr2);
}
XlFileFormat format = XlFileFormat.xlWorkbookDefault;
if (File.Exists(filename))
File.Delete(filename);
DateTime utcNow = DateTime.UtcNow;
Workbook wb = null;
Worksheet ws = null;
Excel xls = new Excel(); // replace with Application.Excel
xls.Visible = true;
xls.DisplayAlerts = false;
if (xls.Workbooks.Count == 0)
wb = xls.Workbooks.Add();
else
wb = xls.Workbooks[1];
if (wb.Worksheets.Count == 0)
ws = wb.Worksheets.Add();
else
ws = wb.Worksheets[1];
int maxCellsPerInsert = 1000000; // inserting too much data at once results in an out of memory exception
int batchSize = maxCellsPerInsert / table.Columns.Count;
int fromIndex = 0;
int n = table.Rows.Count;
while (fromIndex < n) {
int toIndex = Math.Min(fromIndex + batchSize, n);
Range r0 = ws.get_Range("A" + (fromIndex + 1));
Object[,] arr = DataTableUtils.ToObjectArray(table, false, true, null, fromIndex, toIndex); // replace with your own arr[,] code
Range r00 = r0.Resize(arr.GetLength(0), arr.GetLength(1));
r00.Value = arr;
r00.Dispose(); // replace with Marshal.Release
r0.Dispose(); // replace with Marshal.Release
fromIndex = toIndex;
}
wb.SaveAs(filename, format, AccessMode: XlSaveAsAccessMode.xlNoChange);
wb.Close(false, filename, null);
xls.Quit(false, false);
long length = FileEx.GetFileLengthFast(filename);
double totalSeconds = (DateTime.UtcNow - utcNow).TotalSeconds;
String message = "NumRows: " + maxRows + " duration: " + Math.Round(totalSeconds, 1) + " seconds. File length: " + length + " rows/sec: " + Math.Round(1.0* maxRows / totalSeconds);
}
关于excel - 如何在没有 'Out of Memory' 异常的情况下将大型 DataGridView 导出到 Excel?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65779747/
我已经看到 Datagridview 不允许复制和粘贴多个单元格的文本,是否有一个简单的设置来启用它,或者我是否必须使用键处理程序和剪贴板数据存储来包含该功能。 用户想要在一行中复制 3 个单元格,并
我有一个 DataGridView我想限制用户只为特定列下的单元格输入数值的控件。我怎样才能在 DataGridView 中完成这种类型的验证细胞? 当我创建一个简单的文本框时是可能的,但是我如何验证
我正在尝试格式化 DataGridView,使用样式颜色等。DGV 在表单启动时加载(通过 buildGrid 方法),如构造函数代码中所示: public Report1(DataSet d
第一列中的小黑三角的官方DataGridView命名法描述是什么? 好像是标记了DataGridView.CurrentRow的位置,但是只是一个get属性,我想设置一下。 Grid with arr
我有点卡在我写的一些代码上 一个大纲是我正在从 SQL 数据库中读取一些数据,并希望将其显示在表单上的 DataGridView 中。我已经确认有数据从数据库返回,但不确定为什么没有出现。我遵循了互联
我的 DataTable从数据库中提取了三列,而我只需要将其中的两列绑定(bind)到 DataGridView .你能帮我吗? 最佳答案 这可能有用 DataSet ds = new DataSet
我有一个包含 Dataset ds 和 DataGridView dgv 的 WinForms 应用程序。 dgv 绑定(bind)到 ds。 ds 通过 Task() 更新,该 Task() 使用
我有DataGridView(dgHome),在parentForm(Home)中有一些列。子 Form(bill) 有 DataGridView(dgbill) 。我需要当我单击 dgHome 中的
我有两个具有相同列架构的 DataGridView(尽管有两个不同的 DataView 作为 DataSource - 如果这很重要)。将一行从一个数据 GridView 移动到另一个数据 GridV
基本上我有 2 个 DataGridView,我需要将行从一个复制到另一个。 到目前为止我已经尝试过: DataGridViewRowCollection tmpRowCollection = Dat
我正在尝试根据它包含的行数使我的 DataGridView 的高度自动调整。目前,我能够通过以下行完成此操作: dataGridView_SearchResults.AutoSize = true;
使用 WinForms、C# .Net 2.0 我正在向非绑定(bind) DataGridView 添加行。我想在其中一列中有一个 DataGridViewButtonColumn,在单击时删除该行
我有一个 datagridview(dataGridView1,其中包含一个 dataGridview2 列标题),其中我有一个复选框列,称为 dgvCkb,在 dataGridView1。当我在任何
对不起,如果这是一个愚蠢的问题。我在这方面很陌生。我应该如何将组合框添加到数据表,然后将其加载到数据 GridView 中?这可以做到吗?这是最好的方法吗?非常感谢有关如何执行此操作的提示和教程。先感
我正在尝试将 .txt 文件导入 DataGrid。问题是虽然代码大部分工作正常,但在导入 .txt 文件时,它会创建额外的行,例如 this ; OpenFileDialog ofd
datagridview 加载速度非常慢。我该如何优化它? datagridview有4-5千行。 我必须根据几个参数动态生成一个 datagridview。(来自数据库的数据,列数) 我必须从数据库
我怎样才能强制DataGridView.CellValueChanged DataGridViewCell.Value 立即引发事件(并将更改实际提交给 ComboBox 属性)单元格中的编辑控件更改
我有一个通过数据集从数据库中获取数据的功能 public DataSet getDataSet(string query) { DataSet ds = new DataSet()
客户希望能够键入一个字母并让系统滚动 DataGridView,这样具有与字母匹配的单元格的第一行将滚动到 DataGridView 的顶部 任何建议将不胜感激 最佳答案 我不确定您将如何有效地查找与
好吧,我有一个 DataGridView,用户可以在其中单击列标题进行排序。当他们在应用排序时添加新行时,直到验证该行时才会创建记录(在退出 newRow 之前他们无法执行此操作)。排序后如何才能选择
我是一名优秀的程序员,十分优秀!