萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 網路程式設計 >> ASP.NET 程式設計

ASP.NET Core 使用 NPOI 導出/導入 Excel 完整實作 (.xlsx 支援)

NPOI 是 Apache POI 的 .NET 移植版,純 Managed 程式碼、**無需安裝 Office**、**支援 .xls/.xlsx**、跨平台 (Windows/Linux/macOS),是 ASP.NET Core 專案處理 Excel 的首選庫。

NuGet 安裝

Install-Package NPOI

1. 導出 Excel (匯出 DataTable/List 到 .xlsx)

using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel; // .xlsx
using NPOI.HSSF.UserModel; // .xls
using System.Data;

public class ExcelExporter
{
    public byte[] ExportToXlsx(DataTable dt, string sheetName = "Sheet1")
    {
        using (var workbook = new XSSFWorkbook()) // .xlsx
        {
            var sheet = workbook.CreateSheet(sheetName);
            
            // 標題列
            var headerRow = sheet.CreateRow(0);
            for (int i = 0; i < dt.Columns.Count; i++)
            {
                headerRow.CreateCell(i).SetCellValue(dt.Columns[i].ColumnName);
                // 設定標題樣式 (粗體、背景色)
                var style = workbook.CreateCellStyle();
                var font = workbook.CreateFont(); font.IsBold = true;
                style.SetFont(font);
                style.FillForegroundColor = IndexedColors.Grey25Percent.Index;
                style.FillPattern = FillPattern.SolidForeground;
                headerRow.Cells[i].CellStyle = style;
            }

            // 資料列
            for (int r = 0; r < dt.Rows.Count; r++)
            {
                var row = sheet.CreateRow(r + 1);
                for (int c = 0; c < dt.Columns.Count; c++)
                {
                    var cell = row.CreateCell(c);
                    object val = dt.Rows[r][c];
                    SetCellValue(cell, val);
                }
            }

            // 自動調整欄寬
            for (int i = 0; i < dt.Columns.Count; i++) sheet.AutoSizeColumn(i);

            using (var ms = new MemoryStream())
            {
                workbook.Write(ms);
                return ms.ToArray();
            }
        }
    }

    private void SetCellValue(ICell cell, object value)
    {
        if (value == null || value == DBNull.Value) { cell.SetCellValue(""); return; }
        switch (value)
        {
            case string s: cell.SetCellValue(s); break;
            case int i: cell.SetCellValue(i); break;
            case long l: cell.SetCellValue(l); break;
            case double d: cell.SetCellValue(d); break;
            case decimal d: cell.SetCellValue((double)d); break;
            case DateTime dt: cell.SetCellValue(dt); break;
            case bool b: cell.SetCellValue(b); break;
            default: cell.SetCellValue(value.ToString()); break;
        }
    }
}

2. Controller 供下載

[HttpGet]
public IActionResult ExportMembers()
{
    var dt = _memberService.GetExportData(); // 取得 DataTable
    var bytes = new ExcelExporter().ExportToXlsx(dt, "會員名單");
    
    string fileName = $"會員名單_{DateTime.Now:yyyyMMdd_HHmmss}.xlsx";
    return File(bytes, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileName);
}

3. 導入 Excel (上傳 .xlsx 解析驗證入庫)

[HttpPost]
public async Task ImportMembers(IFormFile file)
{
    if (file == null || file.Length == 0) return BadRequest("請選擇檔案");
    if (!file.FileName.EndsWith(".xlsx", StringComparison.OrdinalIgnoreCase)) 
        return BadRequest("僅支援 .xlsx 格式");

    using (var stream = file.OpenReadStream())
    using (var workbook = new XSSFWorkbook(stream))
    {
        var sheet = workbook.GetSheetAt(0); // 第一個工作表
        if (sheet == null) return BadRequest("空的工作表");

        // 驗證標題列
        var headerRow = sheet.GetRow(0);
        if (headerRow == null) return BadRequest("缺少標題列");
        
        string[] expectedHeaders = { "姓名", "簡稱", "分類", "參考價", "商品介紹" };
        for (int i = 0; i < expectedHeaders.Length; i++)
        {
            if (headerRow.GetCell(i)?.ToString() != expectedHeaders[i])
                return BadRequest($"第 {i+1} 欄標題應為 '{expectedHeaders[i]}'");
        }

        var products = new List();
        for (int r = 1; r <= sheet.LastRowNum; r++) // LastRowNum 為最後一行索引 (0-based)
        {
            var row = sheet.GetRow(r);
            if (row == null) continue; // 空行跳過
            if (row.Cells.All(c => c == null || string.IsNullOrWhiteSpace(c.ToString()))) continue; // 全空行跳過

            var product = new Product
            {
                Name = row.GetCell(0)?.ToString()?.Trim(),
                ShortName = row.GetCell(1)?.ToString()?.Trim(),
                Category = row.GetCell(2)?.ToString()?.Trim(),
                RefPrice = decimal.TryParse(row.GetCell(3)?.ToString(), out var p) ? p : 0,
                Description = row.GetCell(4)?.ToString()?.Trim()
            };

            // 業務驗證
            if (string.IsNullOrEmpty(product.Name)) continue; // 或回傳錯誤列表
            if (!_categoryService.Exists(product.Category)) continue; // 分類不存在跳過

            products.Add(product);
        }

        if (products.Count > 0)
            await _productService.BulkInsertAsync(products);

        return Ok(new { Imported = products.Count, Message = "匯入完成" });
    }
}

關鍵技術點總結

項目說明
Workbook 選擇`XSSFWorkbook` (.xlsx, OOXML) / `HSSFWorkbook` (.xls, BIFF8) / `WorkbookFactory.Create(stream)` 自動偵測
儲存格索引0-based (行、列皆從 0 開始)
日期處理`cell.SetCellValue(DateTime)` + `cell.CellStyle.DataFormat = workbook.CreateDataFormat().GetFormat("yyyy/mm/dd")`
公式`cell.SetCellFormula("A1+B1")`,讀取時 `cell.CachedFormulaResultType` 判斷
大量資料效能使用 `SXSSFWorkbook` (串流式,僅保留滾動窗口在記憶體),適用 10萬列以上
樣式重用建立一次 `ICellStyle` 重複套用,避免建立過多 Style 導致檔案損毀 (限制 64000 個)
記憶體管理`using` 區塊確保 `Workbook`、`Stream` 正確釋放

常見問題與解決

  • 中文檔名下載亂碼: `fileName = UrlEncoder.Default.Encode(fileName)` (RFC 5987) 或 `Content-Disposition: attachment; filename*=UTF-8''{encoded}`
  • 數字儲存為文字: 明確呼叫 `SetCellValue(double/int)`,避免 `SetCellValue(string)` 導致 Excel 左上角綠色小三角警告。
  • 公式不計算: NPOI 不計算公式,需 Excel 開啟時重新計算,或使用 `FormulaEvaluator.EvaluateAll()` 僅取得快取值。
  • Linux/Docker 部署: NPOI 純 Managed 完美支援,無相依原生庫。

替代庫比較

庫授權.xlsx.xls效能功能跨平台
NPOIApache 2.0✅✅中全✅
ClosedXMLMIT✅❌中豐富 (LINQ 風格)✅
EPPlusPolyform Noncommercial (商業需付費)✅❌ (5.0+)高最豐富 (樣式、圖表、樞紐)✅
Open XML SDKMIT✅❌高 (底層)基礎 (需自行組裝 XML)✅

NPOI 以免費開源、雙格式支援、跨平台相容,成為 ASP.NET Core 處理 Excel 的標準選擇。掌握 `XSSFWorkbook`、`SXSSFWorkbook` (大數據)、樣式重用與儲存格類型對應,即可覆蓋 95% 匯入匯出需求。

copyright © 萬盛學電腦網 all rights reserved