Generating an excel file with EPPlus is failing - c#

When I try to generate an Excel file using EPPlus, Excel give me the following error message:
Excel cannot open the file 'myfilename.xlsx' because the file format or file extension is not valid. Verify the the file has not been corrupted and that the file extension matches the format of the file.
Here's my code:
public ActionResult Index()
{
using (ExcelPackage package = new ExcelPackage())
{
// I populate the worksheet here. I'm 90% sure this is fine
// because the stream file size changes based on what I pass to it.
var stream = new MemoryStream();
package.SaveAs(stream);
string fileName = "myfilename.xlsx";
string contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
var cd = new System.Net.Mime.ContentDisposition
{
Inline = false,
FileName = fileName
};
Response.AppendHeader("Content-Disposition", cd.ToString());
return File(stream, contentType, fileName);
}
}
Any idea what I'm doing wrong?

All you need to do is reset the stream position. stream.Position = 0;
You shouldn't write directly to the Response, it's not the MVC way. It doesn't follow the correct MVC pipeline and it tightly couples your controller action code to the Response object.
When you add a file name as the 3rd parameter in File(), MVC automatically adds the correct Content-Disposition header... so you shouldn't need to add it manually.
The short of it is, this is what you want:
public ActionResult Index()
{
using (ExcelPackage package = new ExcelPackage())
{
// I populate the worksheet here. I'm 90% sure this is fine
// because the stream file size changes based on what I pass to it.
var stream = new MemoryStream();
package.SaveAs(stream);
string fileName = "myfilename.xlsx";
string contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
stream.Position = 0;
return File(stream, contentType, fileName);
}
}

Your code doesn't show stream being written to the HttpResponse - presumably being done in the File method which you haven't posted.
One way that does work is the following:
Response.Clear();
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader(
"content-disposition", String.Format(CultureInfo.InvariantCulture, "attachment; filename={0}", fileName));
Response.BinaryWrite(package.GetAsByteArray());
Response.End();

Similar to Joe's answer, I still had to call Response.ClearHeaders():
protected void btnDownload_Click(object sender, EventArgs e)
{
ExcelPackage pck = new ExcelPackage();
var ws = pck.Workbook.Worksheets.Add("Sample2");
ws.Cells["A1"].Value = "Sample 2";
ws.Cells["A1"].Style.Font.Bold = true;
var shape = ws.Drawings.AddShape("Shape1", eShapeStyle.Rect);
shape.SetPosition(50, 200);
shape.SetSize(200, 100);
shape.Text = "Sample 2 outputs the sheet using the Response.BinaryWrite method";
Response.Clear();
Response.ClearHeaders();
Response.BinaryWrite(pck.GetAsByteArray());
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("content-disposition", "attachment; filename=Sample2.xlsx");
Response.End();
}

Related

.NET MVC - Generating excel file using OpenXML

I'm having a trouble with generating a .xlsx file from a template which is placed in my project resources. First I'm creating a new ExcelPackage, then opening the template which I'm going to fill with some data - but by now I want to simply download the original xlsx template. I'm trying to get the template without any modifications and download as 'Test.xlsx'. When it's downloaded, after opening the file Excel says that it cannot open my file due to invalid format or extension. Does anyone know what I am doing wrong? When I console.log my response.data then evidently there is some binary data ( about 50kb), but excel cannot show this data properly. Here is some code:
Code from .cshtml :
generateReport(){
this.#http.post(SERVER_URL + "/Mvc/Excel/GenerateReport")
.then(response => {
var blob = new Blob([response.data], { type: 'application/ms-excel' });
var downloadUrl = URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = downloadUrl;
a.download = "Test.xlsx";
document.body.appendChild(a);
a.click();
});
.catch(error => {
console.log('error ', error);
return null;
});
}
Here is my controller action:
[HttpPost]
public void GenerateReport(){
string fileName;
string strfilepath = TemplatePath + "MyTemplate.xlsx";
//TemplatePath - path from my configuration where my xlsx template is stored
using(ExcelPackage p = new ExcelPackage())
{
using(FileStream = new FileStream(strfilepath, FileMode.Open))
{
p.Load(stream);
fileName = "TestReport.xlsx";
Byte[] bin = p.GetAsByteArray();
Response.ClearContent();
Response.Buffer = true;
Response.AddHeader("content-disposition", "attachment; filename=" + fileName);
Response.ContentType = "application/ms-excel";
Response.Charset = "";
Response.BinaryWrite(bin);
Response.Flush();
Response.End();
}
}
}
I think the content type should be application/vnd.ms-excel for exporting excel files.

EPPlus Excel File Not Working with Password

I am creating an Excel file with my MVC application. Depending on a configuration setting, the file will generate requiring a password or without. I can get the file to generate perfect without. But if I implement a password, I get this error:
Excel cannot open the file '' because the file format is not valid. Verify that the file has not been corrupted and that the file extension matches the format of the file.
Any help would be appreciated. Here is my code:
private void ViewExcelPackageFile(Dataset dtList, string filename)
{
//
var contenttype = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
// generate password with another function if configuration requires a password
Response.Clear();
Response.ContentType = contenttype;
Response.AddHeader("content-disposition", "attachment;filename=" + filename);
Response.Cache.SetCacheability(HttpCacheability.NoCache);
using (var stream = new MemoryStream())
{
using (ExcelPackage package = new ExcelPackage())
{
var workbook = package.Workbook;
foreach (var data in dtList)
{
ExcelWorksheet ws = workbook.Worksheets.Add(data.TableName.Replace('/', ' '));
ws.Cells["A1"].LoadFromDataTable(data, true);
}
if (password.Length > 0)
{
package.SaveAs(stream, password);
}
else
{
package.SaveAs(stream);
}
Response.BinaryWrite(stream.ToArray());
Response.Flush();
Response.End();
}
}
}
Please correct anything I'm doing wrong, but again, if I don't apply a password, it works fine. TIA!
Try the following
if (!string.IsNullOrEmpty(password))
{
package.Encryption.Password = password;
}
And then instead of using a MemoryStream, get the bytes directly.
Response.BinaryWrite(package.GetAsByteArray());

Response.end doesn't work

I try to download a .xlsx documnent using Response.End() and OpenXml.
But nothing happens. It's stuck in Response.End()
public void DownloadStudents(int InstituteId, int DepartmentId)
{
var memoryStream = new MemoryStream();
using (var spreadsheetDocument = SpreadsheetDocument.Create(memoryStream, SpreadsheetDocumentType.Workbook))
{
//Some code
foreach (var line in users)
{
// Fill
}
spreadsheetDocument.WorkbookPart.Workbook.Save();
}
var excelName = "Title.xlsx";
Response.Clear();
Response.CacheControl = "Private";
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; Response.AddHeader("Content-Type", "application/octet-stream");
Response.AppendHeader("content-disposition",string.Format("attachment; filename={0}; size={1}", excelName, memoryStream.Length));
Response.BinaryWrite(memoryStream.ToArray());
**//Problem is here.**
Response.End(); // In this line nothing happens
}
The same problem appears when I try use Response.Flush().
If I use CompleteRequest() instead Response.End() .xlsx file is empty
This works for me for all files:
public static void outputFile(FileContentResult file, String contentDisposition)
{
var context = System.Web.HttpContext.Current;
file.FileDownloadName = contentDisposition;
context.Response.Clear();
context.Response.ClearContent();
context.Response.ClearHeaders();
context.Response.AppendHeader("content-disposition", contentDisposition);
context.Response.ContentEncoding = System.Text.Encoding.UTF8;
context.Response.ContentType = file.ContentType;
context.Response.AppendHeader("content-length", file.FileContents.Length.ToString());
context.Response.BinaryWrite(buffer: file.FileContents.ToArray());
context.Response.Flush();
context.ApplicationInstance.CompleteRequest();
}
The FileContentResult is a result of using the File method of the Controller class in MVC.
I also believe what you are missing is closing the workbook before trying to output it.
I would suggest adding spreadsheetDocument.Close() followed by memoryStream.Flush() to your code before trying to output it to the view.
The contentDisposition is simply String.Format("attachment;filename={0}", fileName).
Also, Response.End() throws an error, so always use the other one.

asp.net let user download file with save dialog

I am having some problems with downloading files with the save dialog.
My code executes with no errors but no save dialog shows up. Not in FF, not in chrome and not in IE. The attachment can have the following extensions: .jpg,.jpeg,.png,.bmp,.pdf,.txt,.docx. That's why I use Response.ContentType = "application/octet-stream"; (see in code below).
protected void btnDownload_OnCommand(object sender, CommandEventArgs e)
{
//Get the attachment to download from the webservice
RCX.AddressAttachment attachment = ShopClient.GetAddressAttachment(ServiceContext,
new AddressAttachmentCriteria()
{
Id = (string) e.CommandArgument //= AttachmentID
});
//Get a random filename
var fileName = string.Format("{0}{1}", Guid.NewGuid(), attachment.FileExtension);
//Get the physicalPath to save the file
var physicalPath = string.Format(#"{0}\Attachments\{1}", System.AppDomain.CurrentDomain.BaseDirectory,
fileName);
//Get the webpath to navigate to the file
var webPath = string.Format("{0}Attachments/{1}", Misc.BaseUrl(Request.Url), fileName);
//Create file if not exists
using (new FileStream(physicalPath, FileMode.OpenOrCreate))
{
}
//Write bytes to file
System.IO.File.WriteAllBytes(physicalPath, attachment.Attachment);
var fileInfo = new FileInfo(physicalPath);
//Try to open the save dialog, what is wrong here?
Response.Clear();
Response.ContentType = "application/octet-stream";
Response.AddHeader("Content-Disposition", string.Format("attachment; filename={0};", fileName));
Response.AddHeader("Content-Length", fileInfo.Length.ToString());
Response.WriteFile(physicalPath);
Response.End();
}
The file exists after executing the WriteAllBytes(string path, byte[] bytearray) method.
If I navigate to the webPath in my code, I can also see the file in the browser (see code below).
Response.Redirect(webPath);
What could I be doing wrong?
Many thanks.
Try this
Response.Flush();
before the Response.End();

Error while reading and saving the excel file on serverside

I have tried the below coding for generating excel file on serverside.
C# CODING:
public void ReadandOpenExcel(DirectoryInfo outputDir)
{
//FileInfo newFile = new FileInfo(outputDir.FullName + #"\New Microsoft Excel Worksheet.xlsx");
var ExistFile = Server.MapPath("~/excelsample.xlsx");
var File = new FileInfo(ExistFile);
using (ExcelPackage package = new ExcelPackage(File))
{
package.Load(new FileStream(ExistFile, FileMode.Open));
ExcelWorksheet workSheet = package.Workbook.Worksheets["Sheet1"];
workSheet.Cells["A8"].Value = "kevin";
package.Save();
Response.Clear();
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("Content-Disposition", "attachment; filename=ProposalRequest.xslx");
**Response.BinaryWrite(package.GetAsByteArray());**
// myMemoryStream.WriteTo(Response.OutputStream); //works too
Response.Flush();
Response.Close();
}
}
While running the above code i got an error as : " Package object was closed and disposed, so cannot carry out operations on this object or any stream opened on a part of this package."
ERROR On This Line:
Response.BinaryWrite(package.GetAsByteArray());
Make some way for this coding to move on.
Thanks in advance.
Does it work if you get the bytes before you do the Save ?
Byte[] bin = package.GetAsByteArray();
package.Save();
And then use that value in the Binarywrite;
Response.BinaryWrite(bin);
Maybe it is getting closed on the .Save() call ?

Categories

Resources