6

When reading or modifying some user-created .xlsx files, I get the following error message:

We found a problem with some content in 'test.xlsx'. Do you want us to try to recover as much as we can? If you trust the source of this workbook, click Yes.

Clicking Yes gets me another message:

Excel cannot open the file 'test.xlsx' because the file format or file extension is not valid. Verify that the file has not been corrupted and that the file extension matches the format of the file.

Example of a problem .xlsx file here (before put in NPOI).

Here's the same file, now corrupted after being read from and written back with iWorkbook.Write(filestream); here.

I have no issues creating a new .xlsx file with the following code:

string newPath = @"C:\MyPath\test.xlsx";

using (FileStream fs = new FileStream(newPath, FileMode.Create, FileAccess.Write))
{
    IWorkbook wb = new XSSFWorkbook();
    wb.CreateSheet();
    ISheet s = wb.GetSheetAt(0);
    IRow r = s.CreateRow(0);
    r.CreateCell(0);
    ICell c = r.GetCell(0);
    c.SetCellValue("test");
    wb.Write(fs);
    fs.Close();
}

That works fine.

Even opening one of the problem child .xlsx files, setting it to an IWorkbook and writing it back to the file works:

string newPath = @"C:\MyPath\test.xlsx";

using (FileStream fs = new FileStream(newPath, FileMode.Open, FileAccess.ReadWrite))
{
    IWorkbook wb = new XSSFWorkbook(fs);
    wb.Write(fs);
    fs.Close();
}

However, after running through code that reads from it, gets ISheets, IRows, ICells, etc.... it corrupts the .xlsx file. Even though I specifically removed anything that modifies the workbook. No Creates, Sets, Styles, etc. with NPOI.

I can't really include my code because it would just be confusing, but for the sake of completeness I'm really only using the following types and functions from NPOI during this test:

IWorkbook
XSSFWorkbook
ISheet
IRow
ICell
.GetSheetAt
.GetRow
.GetCell
.LastRowNum

So one of those causes corruption. I would like to eventually set values again and get it working like I have for .xls.

Has anyone experienced this? What are some NPOI functions that could cause corruption? Any input would be appreciated.

Edit: Using NPOI v2.2.1.

justiceorjustus
  • 2,017
  • 1
  • 19
  • 42
  • Can you upload one of those .xlsx files somewhere so we could take a look at it? – Lasse V. Karlsen Dec 12 '16 at 17:03
  • Thanks for the reply. Edited and added https://ufile.io/2b349 as an example. – justiceorjustus Dec 12 '16 at 17:12
  • OK, and so what is the problem with that file? After verifying that it did not contain any macros or whatnot, I opened it in Excel just fine. Are you sure this is not something related to version? I have Excel 2016. – Lasse V. Karlsen Dec 12 '16 at 17:15
  • That's the file before it is read from using NPOI. I can upload the corrupted one after it is run, as well... hold on. – justiceorjustus Dec 12 '16 at 17:17
  • This is the file after it is read and written back: https://ufile.io/9ac96 I tried Excel 2010 and 2013 so far with no good result. – justiceorjustus Dec 12 '16 at 17:19
  • Ok, can you try this, run the code that generates a corrupted file, but ensure you have deleted the target file before you run it, see if that helps. – Lasse V. Karlsen Dec 12 '16 at 17:40
  • I think we may have found the solution here. I will test it a bit more after lunch, but it may have been because I'm overwriting the file I previously opened to read and am now saving it with the filestream. I'm also having success using ``FileAccess.ReadWrite`` if I overwrite the file, as well. I will play around with changing this more. Crossing my fingers! – justiceorjustus Dec 12 '16 at 17:50

4 Answers4

10

I think the problem is that you are reading from, and writing to, the same FileStream. You should be doing the read and write using separate streams. Try it like this:

string newPath = @"C:\MyPath\test.xlsx";

// read the workbook
IWorkbook wb;
using (FileStream fs = new FileStream(newPath, FileMode.Open, FileAccess.Read))
{
    wb = new XSSFWorkbook(fs);
}

// make changes
ISheet s = wb.GetSheetAt(0);
IRow r = s.GetRow(0) ?? s.CreateRow(0);
ICell c = r.GetCell(1) ?? r.CreateCell(1);
c.SetCellValue("test2");

// overwrite the workbook using a new stream
using (FileStream fs = new FileStream(newPath, FileMode.Create, FileAccess.Write))
{
    wb.Write(fs);
}
Brian Rogers
  • 125,747
  • 31
  • 299
  • 300
  • Exactly where I ended up from @Lasse V. Karlsen. I think there is some data left over that NPOI didn't write back correctly and freaked out Excel. The file sizes after NPOI are a little smaller even with no changes. Doing what you said works perfectly. – justiceorjustus Dec 13 '16 at 15:11
  • The really relevant part seems to be that we are overriding the workbook. Just opening the filestream leads to the same error. – Brezelmann Apr 21 '21 at 07:48
4

I had the same problem. In my case the problem was not with the NPOI itself but with its dependency, SharpZipLib.

I used NPOI 2.3.0 and SharpZipLib 1.0.0. and it was given the the same error as in your case. The generated Excel was 0 bytes in size. I downgraded the SharpZipLib back to 0.86.0 in the project where I was using the NPOI (a Service layer) and also in the MVC project(I had the package of SharpZipLib here too).

I also removed manually in web.config the assembly dependency previously created for SharpZipLib:

<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
  .......
  <dependentAssembly>
    <assemblyIdentity name="ICSharpCode.SharpZipLib" publicKeyToken="1b03e6acf1164f73" culture="neutral" />
    <bindingRedirect oldVersion="0.0.0.0-1.0.0.999" newVersion="1.0.0.999" />
  </dependentAssembly>
</assemblyBinding>

I hope this helps someone.

IonutC
  • 607
  • 1
  • 6
  • 11
4

I had the same error attempting to write the excel file to a memory stream and then downloading through my .net Core controller.

This code was my problem (At this point, workbook contained the NPOI excel file I created):

var fileName = $"export.xlsx";
var mimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
MemoryStream stream = new();
workbook.Write(stream);
byte[] output = stream.GetBuffer();
return File(output, mimeType, fileName);

The issue was this line:

byte[] output = stream.GetBuffer();

That line gave me a byte array that contained the contents of my excel file, but I did not realize that the GetBuffer returned not only the byte array representing the excel file, but also the remaining allocated memory for the byte array.

I replaced that line with this:

byte[] output = stream.ToArray();

and life was good.

birwin
  • 2,524
  • 2
  • 21
  • 41
1

When writing back to the file, be sure to use Create as FileMode method. If you use Open, the file will be corrupted because it will concatenate the new file at the end of the old one.

IWorkbook workbook;
using (FileStream file = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
    workbook = new XSSFWorkbook(file);
}

// do things to workbook...

using (FileStream file = new FileStream(filePath, FileMode.Create, FileAccess.Write))
{
    workbook.Write(file);
}
Matthieu Charbonnier
  • 2,794
  • 25
  • 33