NPOI - после сохранения в файл повреждает книгу.xlsx

Я использовал этот код для записи в захватывающий файл Excel. После записи файла, когда я открываю файл вручную, он поврежден. Я использую двоичный файл NPOI 2.3.0.0. Пожалуйста, расскажите, как избежать повреждения Excel.

[Authorize]
public void ExportUsers()
{
    var path = Server.MapPath(@"~\Content\ExportTemplates\") + "sample.xlsx";

    FileStream sw = new FileStream(path, FileMode.Open, FileAccess.Read);

    IWorkbook workbook = WorkbookFactory.Create(sw);       

    ISheet sheet = workbook.GetSheetAt(0);
    IRow row = sheet.GetRow(12);

    ICell cell = row.CreateCell(row.LastCellNum);
    cell.SetCellValue("test");
    workbook.CreateSheet("Ripon");            
    sw.Close();

    using (var exportData = new MemoryStream())
    {
        workbook.Write(exportData);
        string saveAsFileName = string.Format("Export-{0:d}.xls", DateTime.Now).Replace("/", "-");
        System.Web.HttpContext.Current.Response.ContentType = "application/vnd.ms-excel";
        System.Web.HttpContext.Current.Response.AddHeader("Content-Disposition", string.Format("attachment;filename={0}", saveAsFileName));
        System.Web.HttpContext.Current.Response.Clear();
        System.Web.HttpContext.Current.Response.BinaryWrite(exportData.GetBuffer());
        System.Web.HttpContext.Current.Response.End();                    
    }
}

Новый файл создан, но поврежден. Я видел, как люди говорят, что это исправлено в версии 2.0.6, но все еще не работает для меня

1 ответ

Здесь происходит несколько проблем.

Во-первых, вы начинаете с .xlsx файл, но затем изменив расширение загружаемого файла на .xls, .xls а также .xlsx не тот же формат файла; первый является двоичным форматом, а второй - в формате XML в формате zip. Если вы сохраните файл с неправильным расширением, Excel откроет файл как поврежденный при его открытии.

Во-вторых, вы используете неправильный метод для получения данных из MemoryStream, GetBuffer вернет весь выделенный внутренний буферный массив, который будет включать в себя любые неиспользуемые байты, которые находятся за концом данных, если буфер не полностью заполнен. Дополнительные нулевые байты приведут к повреждению загруженного файла. Если вы хотите получить только данные в буфере, вы должны использовать ToArray вместо.

В-третьих, похоже, что вы используете ASP.NET MVC Framework (в зависимости от наличия [Authorize] атрибут вашего метода), но вы напрямую управляете текущим ответом вместо использования встроенного в контроллер File метод возврата загружаемого файла. Я бы порекомендовал использовать встроенные методы там, где это возможно, так как это сделает ваш код намного чище.

Вот исправленный код:

[Authorize]
public ActionResult ExportUsers()
{
    var path = Server.MapPath(@"~\Content\ExportTemplates\") + "sample.xlsx";

    FileStream sw = new FileStream(path, FileMode.Open, FileAccess.Read);

    IWorkbook workbook = WorkbookFactory.Create(sw);

    ISheet sheet = workbook.GetSheetAt(0);
    IRow row = sheet.GetRow(12);

    ICell cell = row.CreateCell(row.LastCellNum);
    cell.SetCellValue("test");
    workbook.CreateSheet("Ripon");
    sw.Close();

    var exportData = new MemoryStream();
    workbook.Write(exportData);
    exportData.Seek(0, SeekOrigin.Begin);  // reset stream to beginning so it can be read

    string saveAsFileName = string.Format("Export-{0:d}.xlsx", DateTime.Now).Replace("/", "-");

    return File(exportData, "application/vnd.ms-excel", saveAsFileName);
}
Другие вопросы по тегам