NPOI 導出excel時,使用AutoSizeColumn報錯

本文解决了一个在使用NPOI库导出Excel时遇到的问题,当调用AutoSizeColumn方法时出现参数无效的异常。通过将NPOI版本从2.1.3.1升级到2.2.1.0,成功解决了AutoSizeColumn导致的System.ArgumentException异常。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

NPOI 導出excel時,使用AutoSizeColumn報如下錯誤:

System.ArgumentException: Parameter is not valid. at System.Drawing.Bitmap..ctor(Int32 width, Int32 height, PixelFormat format) at System.Drawing.Bitmap..ctor(Int32 width, Int32 height) at NPOI.SS.Util.SheetUtil.GetCellWidth(ICell cell, Int32 defaultCharWidth, DataFormatter formatter, Boolean useMergedCells) at NPOI.SS.Util.SheetUtil.GetColumnWidth(ISheet sheet, Int32 column, Boolean useMergedCells) at NPOI.HSSF.UserModel.HSSFSheet.AutoSizeColumn(Int32 column, Boolean useMergedCells) 

解決辦法:

將npoi的版本從2.1.3.1更換成 2.2.1.0,問題解決。

 

參考鏈接:https://stackoverflow.com/questions/19873803/npoi-auto-resize-column

<think>根据用户的问题,错误CS1955通常发生在尝试将不可调用的成员当作方法使用。在之前的代码中,我们使用了`File`方法返回`FileStreamResult`,但用户可能在某个地方错误地使用了`File`属性(这是一个属性,不是方法)。用户的问题可能是混淆了`ControllerBase`的`File`方法(有多个重载)和`File`属性。 在用户提供的代码中,返回文件流的正确方式是使用`File`方法,而不是`File`属性。错误CS1955可能出现在以下情况: 1. 尝试将`File`属性当作方法调用,例如:`File(memoryStream, ...)`,但实际上`File`属性是一个`FileContentResult`类型的属性,不能这样调用。 2. 或者,在某个地方错误地使用了`File`属性。 在之前的代码示例中,我们使用的是`File`方法(继承自`ControllerBase`的方法),它有几个重载。其中一个重载接受`Stream`、`contentType`和`fileDownloadName`。 因此,修复CS1955错误的关键是确保调用的是`ControllerBase.File`方法,而不是访问其`File`属性。 ### 修复步骤: 1. **确认返回语句**:确保返回语句是调用`File`方法,并且传递了正确的参数。 2. **检查方法签名**:确保没有定义名为`File`的局部变量或参数,否则会覆盖`ControllerBase.File`方法。 3. **使用正确的重载**:使用`File(Stream fileStream, string contentType, string fileDownloadName)`重载。 ### 修正后的代码示例: ```csharp [HttpGet("ExportExcel")] public IActionResult ExportExcel() { using (MemoryStream memoryStream = new MemoryStream()) { // ... 创建Excel工作簿并填充数据(代码省略) ... // 将工作簿写入内存流 workbook.Write(memoryStream); memoryStream.Seek(0, SeekOrigin.Begin); // 重置流的位置 // 使用File方法返回文件流 return File( fileStream: memoryStream, contentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileDownloadName: "导出数据.xlsx" ); } } ``` ### 常见错误场景: 1. **错误地将`File`当作属性访问**:例如,写成`return this.File;`,这会导致返回一个`FileContentResult`对象,但你没有提供所需的内容。 2. **局部变量名冲突**:如果在方法内定义了一个名为`File`的变量,那么将无法访问到`ControllerBase.File`方法。例如: ```csharp var File = ...; // 错误!覆盖了File方法 return File(...); // 此File是变量,不是方法 ``` 解决方案:重命名局部变量。 3. **参数名冲突**:同样,如果方法有一个参数名为`File`,也会导致冲突。 ### 其他注意事项: - **流的位置**:在调用`workbook.Write(memoryStream)`后,流的位置在末尾,因此需要将流的位置重置到开始处(`memoryStream.Seek(0, SeekOrigin.Begin)`),否则返回的文件将是空的。 - **内存释放**:使用`using`语句确保`MemoryStream`被正确释放。但是注意,`File`方法返回的`FileStreamResult`会在结果执行读取流,并在完成自动释放流。所以不要在`using`块外使用`memoryStream`,但这里在返回后,`FileStreamResult`会负责流的释放,因此我们不需要额外处理。但是,如果出现异常,`using`块会释放流,而`File`方法可能不会执行,所以这种写法是安全的。 ### 错误CS1955的详细解释: CS1955错误消息是:“非方法‘method’不能像方法一样使用。” 这通常是因为你尝试调用一个不是方法的成员(比如属性、字段)作为方法。例如: ```csharp public class MyController : Controller { // 这是一个属性,不是方法 public string File { get; set; } public IActionResult MyAction() { // 错误:尝试将属性当作方法调用 return File(new byte[0], "text/plain"); } } ``` 在这种情况下,编译器会报错CS1955,因为`File`是属性,不能像方法一样被调用。 ### 解决方案: - 如果是因为在控制器中定义了与基类方法同名的属性或字段,请重命名该成员。 - 确保调用的是基类(`ControllerBase`)的`File`方法。 如果问题仍然存在,请检查控制器的基类以及是否引入了其他可能冲突的命名空间。 ### 前端代码调整: 前端代码不需要调整,因为后端返回的是文件流,前端通过Blob处理下载。 ### 总结: 修复CS1955错误的关键是确保调用的是正确的`File`方法(来自`ControllerBase`),并且没有因局部变量、参数或成员同名而导致的冲突。同,确保流的位置正确,以便返回完整的Excel文件。 §§相关问题§§ 1. 如何解决C#中方法名与属性名冲突的问题? 2. 在ASP.NET Core中返回文件流如何正确释放资源? 3. 如何在前端处理Excel文件下载失败的情况?</think># 解决C#导出Excel文件流错误CS1955 错误CS1955通常发生在尝试将不可调用的成员当作方法使用。在导出Excel文件流的场景中,这通常是因为混淆了`File`方法和`File`属性导致的。让我们一步步解决这个问题。 ## 错误原因分析 错误CS1955的根本原因是: - 尝试调用一个不是方法的成员(如属性) - 在代码中错误地将`File`属性当作方法使用 - 混淆了`ControllerBase.File`方法(用于返回文件)和`File`属性 ## 修复后的完整代码 ```csharp using Microsoft.AspNetCore.Mvc; using NPOI.SS.UserModel; using NPOI.XSSF.UserModel; using System.IO; [ApiController] [Route("api/[controller]")] public class ExcelExportController : ControllerBase { [HttpGet("Export")] public IActionResult ExportExcel() { try { // 1. 创建内存流和工作簿 using (var memoryStream = new MemoryStream()) { // 2. 创建Excel工作簿(使用xlsx格式) IWorkbook workbook = new XSSFWorkbook(); // 3. 创建工作表并填充数据 ISheet sheet = workbook.CreateSheet("数据报表"); // 创建标题行 IRow headerRow = sheet.CreateRow(0); headerRow.CreateCell(0).SetCellValue("ID"); headerRow.CreateCell(1).SetCellValue("名称"); headerRow.CreateCell(2).SetCellValue("创建日期"); // 填充示例数据 for (int i = 1; i <= 100; i++) { IRow row = sheet.CreateRow(i); row.CreateCell(0).SetCellValue(i); row.CreateCell(1).SetCellValue($"项目{i}"); row.CreateCell(2).SetCellValue(DateTime.Now.AddDays(-i).ToString("yyyy-MM-dd")); } // 4. 自动调整列宽 for (int i = 0; i < 3; i++) { sheet.AutoSizeColumn(i); } // 5. 将工作簿写入内存流 workbook.Write(memoryStream); memoryStream.Seek(0, SeekOrigin.Begin); // 6. 正确使用File方法返回流(修复CS1955的关键) return File( fileStream: memoryStream, contentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileDownloadName: $"导出数据_{DateTime.Now:yyyyMMddHHmm}.xlsx" ); } } catch (Exception ex) { // 错误处理 return StatusCode(500, $"导出失败: {ex.Message}"); } } } ``` ## 关键修复点 ### 1. 正确使用File方法而非File属性 错误CS1955的核心是混淆了: ```csharp // 错误用法(导致CS1955) return this.File; // 这是属性访问 // 正确用法(调用方法) return File(stream, contentType, fileName); ``` ### 2. 确保流位置重置 在写入流后必须重置位置: ```csharp memoryStream.Seek(0, SeekOrigin.Begin); // 重置流位置到开头 ``` ### 3. 正确的MIME类型 根据Excel格式使用正确的MIME类型: - xlsx: `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` - xls: `application/vnd.ms-excel` ## 前端下载实现(JavaScript) ```javascript async function downloadExcel() { try { const response = await fetch('/api/ExcelExport/Export'); if (!response.ok) { throw new Error(`导出失败: ${response.status}`); } // 获取文件名 const contentDisposition = response.headers.get('Content-Disposition'); const fileName = contentDisposition ? contentDisposition.split('filename=')[1].replace(/"/g, '') : `export_${new Date().getTime()}.xlsx`; // 创建Blob对象 const blob = await response.blob(); // 创建下载链接 const downloadUrl = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = downloadUrl; link.download = fileName; link.style.display = 'none'; // 触发下载 document.body.appendChild(link); link.click(); // 清理资源 setTimeout(() => { document.body.removeChild(link); URL.revokeObjectURL(downloadUrl); }, 100); } catch (error) { console.error('导出错误:', error); alert('文件导出失败: ' + error.message); } } // 绑定按钮点击事件 document.getElementById('exportBtn').addEventListener('click', downloadExcel); ``` ## 常见错误及解决方案 | 错误类型 | 原因 | 解决方案 | |---------|------|---------| | **CS1955** | 将`File`属性当作方法调用 | 使用`File()`方法而非`File`属性 | | **文件为空** | 流位置未重置 | 添加`memoryStream.Seek(0, SeekOrigin.Begin)` | | **中文乱码** | 文件名未正确编码 | 使用`filename*=UTF-8''`格式 | | **内存溢出** | 大数据量处理不当 | 分块处理数据或使用SAX模式[^1] | ## 中文文件名处理 ```csharp // 在返回文件前添加文件名编码处理 var fileName = $"导出数据_{DateTime.Now:yyyyMMddHHmm}.xlsx"; var encodedFileName = Uri.EscapeDataString(fileName); Response.Headers.Add("Content-Disposition", $"attachment; filename*=UTF-8''{encodedFileName}"); ``` ## 性能优化建议 1. **分块处理大数据**:当数据量超过10万行,分批写入 ```csharp const int batchSize = 5000; for (int i = 0; i < totalRecords; i += batchSize) { var batch = data.Skip(i).Take(batchSize); // 处理批次数据... } ``` 2. **使用SAX模式**:对于超大Excel文件,使用事件驱动模型[^1] 3. **内存流分块刷新**:定期刷新内存流到输出 ```csharp if (memoryStream.Length > 10 * 1024 * 1024) // 每10MB刷新 { memoryStream.WriteTo(Response.Body); memoryStream.SetLength(0); } ``` ## 总结 修复CS1955错误的关键在于正确区分`ControllerBase.File`方法和`File`属性。通过使用`File(stream, contentType, fileName)`方法,并确保流位置正确重置,可以成功导出Excel文件流。前端通过Blob API处理返回的二进制流,实现文件下载功能。 对于大型Excel文件,建议采用分块处理或SAX模式避免内存溢出问题。同正确处理中文文件名编码,确保用户下载体验。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值