使用 OWC (Office Web Components) 的 SpreadsheetClass 導出數據時,原始代碼逐行逐列賦值 f_sheet.Cells[row, col] = value,1000 筆資料耗時近 10 分鐘,甚至超時。每次儲存格賦值都是一次 COM 跨進程調用,開銷極大。
關鍵洞察:OWC 的 Range.set_Value 支援一次將二維陣列寫入整個區塊,將 N×M 次 COM 調用減為 1 次。
foreach (DataRow row in dt.Rows)
{
foreach (DataColumn col in dt.Columns)
{
if (row[col] != DBNull.Value)
{
// 逐儲存格賦值 - 極慢
sheet.Cells[currentRow, currentCol] =
isTextColumn ? "'" + row[col].ToString() : row[col];
}
currentCol++;
}
currentRow++;
currentCol = startCol;
}
public void WriteDataToSpreadsheet(SpreadsheetClass spreadsheet, DataTable dt,
int startRow, int startCol, bool withCaption, int startRecord,
int limited, int[] textColumns)
{
Worksheet sheet = spreadsheet.ActiveSheet;
int maxRow = 65535;
int totalRow = 0, rowCount = 0;
int currentRow = startRow, currentCol = startCol;
// 1. 處理標題列
if (withCaption)
{
foreach (DataColumn col in dt.Columns)
{
sheet.Cells[currentRow, currentCol++] = col.Caption;
}
currentRow++; totalRow++;
currentCol = startCol;
}
// 2. 預計算列名 (A, B, ..., Z, AA, AB...)
string[] A2Z = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".Select(c => c.ToString()).ToArray();
var colNames = new List<string>(A2Z);
foreach (var c1 in A2Z)
foreach (var c2 in A2Z)
colNames.Add(c1 + c2);
string lastColName = colNames[dt.Columns.Count - 1];
// 3. 將所有數據先裝入 List<object[,]>
var dataBlocks = new List<object[,]>();
int rows = dt.Rows.Count, cols = dt.Columns.Count;
for (int i = 0; i < rows; i++)
{
var block = new object[1, cols]; // 單行區塊
for (int j = 0; j < cols; j++)
{
var val = dt.Rows[i][j];
if (val != DBNull.Value)
{
block[0, j] = Array.IndexOf(textColumns, j) >= 0
? "'" + val.ToString() : val;
}
else block[0, j] = "";
}
dataBlocks.Add(block);
}
// 4. 批次寫入:每行一個 Range.set_Value 調用
object missing = System.Reflection.Missing.Value;
for (int i = 0; i < rows; i++)
{
// 目標區塊:單行完整列範圍
Range targetRange = sheet.get_Range(
"A" + (currentRow).ToString(),
lastColName + (currentRow).ToString());
// 一次性寫入整行陣列 - 核心提速點
targetRange.set_Value(missing, dataBlocks[i]);
rowCount++; totalRow++; currentRow++;
if (totalRow >= maxRow) break;
if (limited > 0 && rowCount >= limited) break;
}
}
| 指標 | 優化前 | 優化後 | 提升 |
|---|---|---|---|
| 1000 筆 | ~10 分鐘 | ~2 秒 | 300x |
| 60000 筆 | 超時/崩潰 | ~2 分鐘 | 可用 |
| COM 調用次數 | Rows × Cols | Rows (+1 標題) | 降低 99%+ |
object[,] 維度必須與目標 Range 完全一致'123 強制 Excel 視為文本,避免科學記號或前導零丟失「用陣列換迴圈」是 COM 互操作優化的黃金法則,這個技巧在 Excel Interop、Word Interop 同樣有效。