.NET Core 6使用NPOI导出Excel提示文件格式或扩展名无效
.NET Core 6导出Excel打开提示格式无效问题
问题描述
在.NET Core 6中创建返回Excel文件的接口端点,下载生成的Excel文件后打开,系统提示文件格式或扩展名无效,实现代码如下:
Controller代码
[HttpPost("/api/report")] public async Task ExportFile([FromBody] ReportDTO reportDTO) { _excelService.GenerateReport(HttpContext, reportDTO); }
Service代码
public void GenerateReport(HttpContext httpContext, ReportDTO reportDTO) { IWorkbook workbook; workbook = new XSSFWorkbook(); ISheet excelSheet = workbook.CreateSheet("Test"); IRow row = excelSheet.CreateRow(0); row.CreateCell(0).SetCellValue("Test"); row.CreateCell(1).SetCellValue("Hello"); row = excelSheet.CreateRow(1); row.CreateCell(0).SetCellValue(1); row.CreateCell(1).SetCellValue("World"); workbook.WriteExcelToResponse(httpContext, GetFileName(reportDTO)); }
扩展方法代码
public static void WriteExcelToResponse(this IWorkbook book, HttpContext httpContext, string templateName) { var response = httpContext.Response; response.ContentType = "application/vnd.ms-excel"; if (!string.IsNullOrEmpty(templateName)) { var contentDisposition = new Microsoft.Net.Http.Headers.ContentDispositionHeaderValue("attachment"); contentDisposition.SetHttpFileName(templateName); response.Headers[HeaderNames.ContentDisposition] = contentDisposition.ToString(); } book.Write(response.Body); }
故障原因
代码存在两处核心错误:
- MIME类型与文件格式不匹配:
XSSFWorkbook生成的是OOXML格式的.xlsx文件,但代码中配置的application/vnd.ms-excel是旧版二进制.xls格式对应的MIME类型,格式不匹配会触发Office的文件校验警告。 - 响应流写入逻辑不规范:接口方法是异步签名,但内部采用同步方式直接写入HttpContext响应流,既没有等待写入完成,也没有正确释放Workbook资源,极易出现流写入不完整,导致文件损坏。
修复方法
- 修正格式匹配关系:使用
XSSFWorkbook时,对应MIME类型改为application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,返回文件名后缀统一使用.xlsx;如果要生成.xls格式文件,将XSSFWorkbook替换为HSSFWorkbook,保留原有MIME类型,文件名后缀用.xls即可,两种格式不要混用。 - 调整流写入逻辑,不要直接在Service层操作HttpContext响应流,改为先将Workbook写入内存流生成字节数组,再通过Controller自带的
File方法统一返回,自动处理响应头和流写入的完整生命周期,避免文件损坏。
修正后的代码参考:
// 调整扩展方法,生成Excel字节数组,不再直接操作响应流 public static byte[] GetExcelByteArray(this IWorkbook book) { using var ms = new MemoryStream(); book.Write(ms, true); // 传入leaveOpen:false参数,写入完成后自动释放Workbook资源 return ms.ToArray(); }
// Service层调整,不再接收HttpContext参数,直接返回文件字节数组 public byte[] GenerateReport(ReportDTO reportDTO) { using IWorkbook workbook = new XSSFWorkbook(); ISheet excelSheet = workbook.CreateSheet("Test"); IRow row = excelSheet.CreateRow(0); row.CreateCell(0).SetCellValue("Test"); row.CreateCell(1).SetCellValue("Hello"); row = excelSheet.CreateRow(1); row.CreateCell(0).SetCellValue(1); row.CreateCell(1).SetCellValue("World"); return workbook.GetExcelByteArray(); }
// Controller层调整,返回IActionResult,用内置File方法返回文件 [HttpPost("/api/report")] public async Task<IActionResult> ExportFile([FromBody] ReportDTO reportDTO) { var fileBytes = _excelService.GenerateReport(reportDTO); var fileName = GetFileName(reportDTO); return File(fileBytes, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileName); }
注意:不要在异步Action中直接同步往Response.Body写数据,ASP.NET Core的请求响应管道有自己的异步写入调度逻辑,直接同步写入大概率会出现内容截断的问题。
内容的提问来源于stack exchange,提问作者Ronnie
相关产品推荐
相关产品推荐

