You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用HSSFWorkbook导出Excel本地正常但测试环境异常的问题求助

问题排查与解决方案

核心问题分析

你遇到的问题本质是Razor视图渲染机制与文件流输出的冲突,加上IIS环境下的路径/权限、异常吞吃等隐性问题:

  1. Razor视图本身是为HTML渲染设计的,即使手动写入Response.Body,IIS环境中可能会追加布局页内容、空白字符等额外输出,导致Excel流被污染,最终变成"网页内容"。
  2. 模板文件在IIS下可能存在路径拼接错误或权限不足,导致读取失败,但异常被完全吞吃,无法定位问题。
  3. 直接操作Response.Body的方式不符合ASP.NET Core的响应处理规范,容易出现流写入不完整、响应终止不彻底的情况。

解决方案

1. 将Excel生成逻辑迁移到Controller,返回标准FileResult

放弃用Razor视图处理文件下载,改用ASP.NET Core推荐的FileStreamResult方式,确保响应内容纯净。

修改后的ReportController.cs:

using System.IO;
using Microsoft.AspNetCore.Mvc;
using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;

[HttpGet]    
public IActionResult ExportSummaryToExcel(string startDate, string endDate, string postalCode)
{
    string strStartDate = startDate;
    string strEndDate = endDate;
    string strRequestedPostalCode = postalCode;

    // 获取业务数据
    DataTable rsReportDataByPostalCode = _summaryByAgeStagePostalCodeService
        .GetSummaryByPostalCode($"{strStartDate} 00:00:00.000000000", $"{strEndDate} 23:59:59.000000000", strRequestedPostalCode);

    // 拼接模板文件路径(用Path.Combine避免系统分隔符问题)
    string templatePath = Path.Combine(_hostingEnvironment.ContentRootPath, "Views", "Reports", "SummaryByAgeStagePostalBlank.xls");
    if (!System.IO.File.Exists(templatePath))
    {
        return NotFound("Excel模板文件不存在");
    }

    try
    {
        using (var templateStream = new FileStream(templatePath, FileMode.Open, FileAccess.Read))
        using (var wb = new HSSFWorkbook(templateStream))
        {
            ISheet sheet = wb.GetSheetAt(0);

            // 设置Excel标题
            sheet.GetRow(0).GetCell(0).SetCellValue($"Summary Data For Postal Code {strRequestedPostalCode}");
            sheet.GetRow(0).GetCell(4).SetCellValue($"Start Date: {strStartDate}");
            sheet.GetRow(0).GetCell(7).SetCellValue($"End Date: {strEndDate}");

            // 填充数据行
            if (rsReportDataByPostalCode != null && rsReportDataByPostalCode.Rows.Count > 0)
            {
                int currentRowNumber = 2;
                foreach (DataRow row in rsReportDataByPostalCode.Rows)
                {
                    sheet.GetRow(currentRowNumber).GetCell(0).SetCellValue(row["Stage"].ToString());
                    sheet.GetRow(currentRowNumber).GetCell(1).SetCellValue(row["<20"].ToString());
                    sheet.GetRow(currentRowNumber).GetCell(2).SetCellValue(row["20-24"].ToString());
                    sheet.GetRow(currentRowNumber).GetCell(3).SetCellValue(row["25-29"].ToString());
                    sheet.GetRow(currentRowNumber).GetCell(4).SetCellValue(row["30-34"].ToString());
                    sheet.GetRow(currentRowNumber).GetCell(5).SetCellValue(row["35-39"].ToString());
                    sheet.GetRow(currentRowNumber).GetCell(6).SetCellValue(row["40-44"].ToString());
                    sheet.GetRow(currentRowNumber).GetCell(7).SetCellValue(row["45-49"].ToString());
                    sheet.GetRow(currentRowNumber).GetCell(8).SetCellValue(row["50-54"].ToString());
                    sheet.GetRow(currentRowNumber).GetCell(9).SetCellValue(row["55-59"].ToString());
                    sheet.GetRow(currentRowNumber).GetCell(10).SetCellValue(row["60-64"].ToString());
                    sheet.GetRow(currentRowNumber).GetCell(11).SetCellValue(row["65-69"].ToString());
                    sheet.GetRow(currentRowNumber).GetCell(12).SetCellValue(row["70-74"].ToString());
                    sheet.GetRow(currentRowNumber).GetCell(13).SetCellValue(row["75-79"].ToString());
                    sheet.GetRow(currentRowNumber).GetCell(14).SetCellValue(row["80-84"].ToString());
                    sheet.GetRow(currentRowNumber).GetCell(15).SetCellValue(row["85-89"].ToString());
                    sheet.GetRow(currentRowNumber).GetCell(16).SetCellValue(row["90-94"].ToString());
                    sheet.GetRow(currentRowNumber).GetCell(17).SetCellValue(row["95-99"].ToString());
                    sheet.GetRow(currentRowNumber).GetCell(18).SetCellValue(row["100+"].ToString());

                    currentRowNumber++;
                }
            }

            // 将Excel写入内存流后返回
            using (var memoryStream = new MemoryStream())
            {
                wb.Write(memoryStream);
                memoryStream.Position = 0;
                return File(memoryStream, "application/vnd.ms-excel", $"Summary.xls");
            }
        }
    }
    catch (Exception ex)
    {
        // 记录错误日志(注入ILogger<ReportController> _logger)
        _logger.LogError(ex, "导出Excel失败");
        return StatusCode(500, "导出失败,请联系管理员");
    }
}

2. 修复模板文件的部署与权限问题

  • 在项目中选中模板文件SummaryByAgeStagePostalBlank.xls,设置复制到输出目录为「始终复制」或「如果较新则复制」,确保发布到IIS时文件被同步。
  • 检查IIS应用程序池的运行身份,确保它具有读取模板文件所在目录的权限。

3. 移除原Razor视图_ExportSummaryToExcel.cshtml

现在逻辑全部在Controller中完成,无需保留该视图,避免混淆。


内容的提问来源于stack exchange,提问作者Fokwa Best

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 18:53:13