I have NPOI 2.2.1 and I realized that when generating XLSX file and opening with Excel 2013, a message box telling that a problem was encountered but Excel could try to recover from it. When I click "Yes", the sheet is finally shown.
This is the code:
IWorkbook workbook = new XSSFWorkbook();
ISheet sheet = workbook.CreateSheet(this.Title);
using (var exportData = new MemoryStream())
{
workbook.Write(exportData);
return exportData.GetBuffer();
}
As you see, I am only creating the workbook, adding a sheet and then returning the bytes array. That array is stored in file using this code:
string targetFile = System.IO.Path.Combine(System.IO.Path.GetTempPath(), String.Concat(btnExportar.Tag, "_", DateTime.Now.ToString("yyyyMMddHHmmss"), ".xlsx"));
System.IO.File.WriteAllBytes(targetFile, xls.GetExcelData());
With XLS files there is no problem.
Regards
Jaime
The problem is not with NPOI, but with your use of GetBuffer(). That is not the method you want to use for this purpose, as GetBuffer() returns the raw buffer underlying the memory stream (usually oversized), without cutting it off at the current position of the stream. This will leave uninitialized data at the end, causing the error message in Excel.
The get all the bytes from a MemoryStream, use ToArray():
using (var exportData = new MemoryStream())
{
workbook.Write(exportData);
// return exportData.GetBuffer();
return exportData.ToArray();
}
Related
My customer has a use case for exporting search results to a spreadsheet. I would like to return a formatted spreadsheet to them, but the only way I can get the formatting changes to "stick" is by calling
workbook.Save(memoryStream, SaveFormat.Xlsx);
The problem with calling the method above, is that a spreadsheet will actually be saved to my local project folder, which is not desired behavior. How can I return the spreadsheet without calling workbook.Save()?
public byte[] ExportSpreadsheet(List<Result> results)
{
var workbook = MakeWorkbook(results);
var memoryStream = new MemoryStream();
workbook.Save(memoryStream, SaveFormat.Xlsx); // this saves the spreadsheet in the project
memoryStream.Seek(0, SeekOrigin.Begin);
var byteArray = memoryStream.ToArray();
return byteArray;
}
private Workbook MakeWorkbook(List<Result> results)
{
var workbook = new Workbook();
AddDataToWorkbook(workbook);
ApplyFormattingAfterData(workbook);
return workbook;
}
workbook.Save(memoryStream, SaveFormat.Xlsx);
You are doing ok. This line will save the workbook to stream and not on physical filepath. It won't save to your project's folder or path.
PS. I am working as Support developer/ Evangelist at Aspose.
My objective is to write some data into an excel.
Here i am opening a file with file stream by exclusive lock (FileMode.Open, FileShare.Read etc., I need to lock the file to restrict others writing into excel while i am processing.) then writing some content into it and finally close the stream, so that other threads can write into this file. I am using EPPlus(5.7.4) version.
The code i am using here is :
public void WriteToExcel()
{
using (var stream = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.Read))
using (var excelPackage = new ExcelPackage(stream))
{
DoSomething(excelPackage);
excelPackage.SaveAs(stream);
stream.Close();
}
}
public void DoSomething(ExcelPackage excelPackage)
{
var cell = excelPackage.Workbook.Worksheets[0].Cells[2, 3];
cell.Value = "some value";
}
I put a break point in using statement and opened excel in the middle of execution and it showing a message saying like below which is correct.
But once i finish with execution when i try to open excel file it showing below error message.
We found a problem with some content in Sample.xlsx. Do you want us to try to recover as much as we can? if you trust the source of this book, Click Yes
I tried in different ways but none worked for me, as same error message is displaying. Can someone help me resolving this issue.
The problem is that you're reading from and rewriting to the same file stream simultaneously.
You can test this by changing excelPackage.SaveAs(new FileInfo("Book2.xlsx")); and create a new file - your file will be created without any issues.
You could open your original document, write the changes to a new file, then delete the original file and rename the new file back to the original name:
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
using (var stream = new FileStream("Book1.xlsx", FileMode.Open, FileAccess.ReadWrite, FileShare.Read))
using (var excelPackage = new ExcelPackage(stream))
{
DoSomething(excelPackage);
excelPackage.SaveAs(new FileInfo("Book2.xlsx"));
}
File.Delete("Book1.xlsx");
File.Move("Book2.xlsx", "Book1.xlsx");
The caveat with this is that if you have multiple things trying to access that file, then they might throw FileNotFound exceptions if they happen to try to open Book1.xlsx after it's delete and before Book2.xlsx is renamed.
That said, if you're dealing with that level of parallelism then you shouldn't be using a Excel file.
Side note: You don't need stream.Close(); as the using block automatically closes the stream.
Below code useful to me, you can refer it.
public void WriteToExcel()
{
string path = #"C:\Use**op\aa.xlsx";
FileInfo file = new FileInfo(path);
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
using (ExcelPackage package = new ExcelPackage(file))
{
DoSomething(package);
}
}
public void DoSomething(ExcelPackage package)
{
ExcelWorksheet worksheet = package.Workbook.Worksheets[0];
worksheet.Cells[2,4].Value = "some value";
package.Save();
}
i'am trying to get an xls file from an ZipArchive but cant get it with EPPLUS
foreach (ZipArchiveEntry entry in archive.Entries)
{
if (entry != null)
{
string filepath = entry.FullName;
FileInfo fileInfo = new FileInfo(filepath);
//here i got the excel package with the xls file inside the excelPackage
using (ExcelPackage excelPackage = new ExcelPackage(fileInfo))
{
//but here impossible de get the worksheet or workbook inside or anything else
ExcelWorksheet worksheet = excelPackage.Workbook.Worksheets.FirstOrDefault();
int totalColomn = worksheet.Dimension.End.Column;
int nbrsheet = excelPackage.Workbook.Worksheets.Count();
}
}
}
the ExcelPackage i get in debug
i see the xls file on debug inside the excelpackage but just when i try to get worksheet it exit without exception code....
same here when trying with entryStream
using (var entryStream = entry.Open())
{
//Cant even get the excelpackage, it crash here without exception
using (ExcelPackage excelPackage = new ExcelPackage(entryStream))
{
ExcelWorksheet worksheetest = excelPackage.Workbook.Worksheets.FirstOrDefault();
}
}
the stream here seem also strange ...
entryStream Debug
Working with .NET CORE Blazor ServerSide, ePPLUS 4.5
Thanks for helping
entry.FullName refers to the full path to the file inside the zip archive, while FileInfo describes a file in the filesystem of the OS, which is a completely different thing. You haven't extracted anything to the OS filesystem yet, so the FileInfo won't refer to a file that actually exists.
Try the ExcelPackage constructor that takes a Stream, which you can get directly from a ZipArchiveEntry:
using (var entryStream = entry.Open())
{
using (ExcelPackage excelPackage = new ExcelPackage(entryStream))
{
// ...
}
}
I find the problem.
it was that i tried to get an xls file and the epplus library dont work with it...
you have to be careful, EPplus dont work with xls file
So , your solution Jeff is working, it was my fault, didn't specified the extension of my excel file... sorry
-> EPlus with an .xlsx OK, not .xls
My bad.
Thanks anyway :-)
I used this code to write to an exciting excel file. After writing the file, when I open the file manually it is corrupted. I am using NPOI binary 2.3.0.0 Please tell how to avoid excel getting corrupted.
[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();
}
}
New file is created but corrupted. I've seen people say this is fixed in version 2.0.6, but still not working for me
There are several problems going on here.
First, you are starting with an .xlsx file but then changing the download file extension to .xls. .xls and .xlsx are not the same file format; the former is a binary format, while the latter is a zipped XML format. If you save the file with the wrong extension, then Excel will report the file as corrupted when it is opened.
Second, you are using the wrong method to get the data from the MemoryStream. GetBuffer will return the entire allocated internal buffer array, which will include any unused bytes that are beyond the end of the data if the buffer is not completely full. The extra null bytes will cause the downloaded file to be corrupted. If you want to get just the data in the buffer then you should use ToArray instead.
Third, it looks like you are using the ASP.NET MVC framework (based on the presence of the [Authorize] attribute on your method) but you are directly manipulating the current response instead of using the controller's built-in File method for returning a downloadable file. I would recommend using the built-in methods where possible, as it will make your code much cleaner.
Here is the corrected code:
[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);
}
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.
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);
}
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.
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.
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);
}