I'm working on adding Export functionality to an existing site. I've created an Excel file, and I'm able to output it to my local directory, but when I try pushing the file to the user using Response, nothing happens. I've viewed several differnet articles and Stack questions, and none seem to be different from what I have, or work when I apply them.
Here is the code I currently have:
public void Export () {
//Create instance of the database
ExportContext eDB = new ExportContext();
var query = (from x in eDB.EMPLOYEES
select x);
IEnumerable<EMPLOYEES> employees = query.ToList();
//Set filename to Table + date/time
string fileName = "EMPLOYEES" + DateTime.Now.ToShortTimeString() + ".xlsx";
//Create excel package
ExcelPackage excel = new ExcelPackage();
var worksheet = excel.Workbook.Worksheets.Add("Employees");
worksheet.Cells[1, 1].LoadFromCollection(employees, true);
using (var memoryStream = new MemoryStream()) {
//Output the file to the user via download
Response.Buffer = false;
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("content-disposition", "attachment; filename=" + fileName);
excel.SaveAs(memoryStream);
memoryStream.WriteTo(Response.OutputStream);
Response.Flush();
Response.Close();
Response.End();
}
}
Any help will be appreciated. Thanks.
Related
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.
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 ?
I generate Excel document in ASP.Net with below code:
if (dataTable.Rows.Count > 0)
{
var tw = new StringWriter();
var hw = new System.Web.UI.HtmlTextWriter(tw);
var dgGrid = new DataGrid();
dgGrid.DataSource = dataTable;
dgGrid.DataBind();
dgGrid.RenderControl(hw);
attachment = "attachment; filename=" + fileName + ".xls";
Response.Charset = "UTF-8";
Response.ContentType = "application/vnd.ms-excel";
Response.AppendHeader("content-disposition", attachment);
outputResponse = tw.ToString();
}
But when I want to open Excel document it gives error:
Error: excel cannot open the file .xls because the file format or
file extension is not valid
How can I solve it?
Either use a reference to the Excel Application object to create an actual excel file, use one of the many codeplex based projects to create an Excel spreadsheet or most easily create a CSV file from your datagrid and return that, which many users computers will prompt them to open in Excel.
Just tried your code, with exception of the last line, which I've changed:
var tw = new StringWriter();
var hw = new System.Web.UI.HtmlTextWriter(tw);
var dgGrid = new DataGrid();
dgGrid.DataSource = dataTable;
dgGrid.DataBind();
dgGrid.RenderControl(hw);
var attachment = "attachment; filename=" + fileName + ".xls";
Response.Charset = "UTF-8";
Response.ContentType = "application/vnd.ms-excel";
Response.AppendHeader("content-disposition", attachment);
// outputResponse = tw.ToString();
Response.Output.Write(tw.ToString());
Response.Flush();
Response.End();
My datatable is a 2-col table with simple data (letters and numbers)and it is opened as it should.
You will get the message that says:
The file you are trying to open'yourfilename.xls', is in a different
format thahn specified by the file extension. Verify that the file is
not corrupted and is from a trusted source before opening the file. Do
you want to open the file now?
After clicking OK the file opens just fine.
I'm creating/sending a file with EPPlus like this:
using (ExcelPackage package = new ExcelPackage())
{
ExcelWorksheet worksheet = package.Workbook.Worksheets.Add("Sheet");
... //create file here
Response.Clear();
Response.ContentType = "application/xlsx";
Response.AddHeader("content-disposition", "attachment; filename=" + filename + ".xlsx");
Response.BinaryWrite(package.GetAsByteArray());
Response.End();
}
Everything works fine, except the file always comes back as read-only, and I can't figure out why. Within the "package" object, there is a file object with a IsReadOnly field, but the file object is null. I anticipate that I'm not creating the xcel file correctly, but this is the only way I could figure out how to create the file well. Initially I was using a memory stream, but doing that, I ran into issues when the excel file was bigger than 50 rows.
Edit/update: So I initiate the code block by clicking a button "Download as Excel File". The code runs, creating the file, and the user is prompted with a "You have chose to open: thisismyexcelfile.xlsx which is a: XLSX file (size here) from: mywebsite. What should firefox do with this file?" After selecting "Open with OpenOffice Calc" the spreedsheet opens and displays appropriately but is read-only.
Edit/update: I checked the file properties with OpenOffice. Under Properties/Security there is a "Open file Read Only" checkbox, but it is already unchecked and disabled.
I found this example, I see 3 main differences
Response.ContentType is set to application/vnd.openxmlformats
Response.WriteFile is used instead or Response.BinaryWrite
In this example the file is being saved to the server, sent as response and then deleted
void ExportToExcel(Event evt)
{
var fileInfo = new FileInfo(Path.GetTempPath() + "\\" +
DateTime.Now.Ticks + ".xlsx");
using (var xls = new ExcelPackage(fileInfo))
{
var sheet = xls.Workbook.Worksheets.Add(evt.Title);
sheet.Cell(1, 1).Value = "First name";
sheet.Cell(1, 2).Value = "Last name";
sheet.Cell(1, 3).Value = "E-mail";
sheet.Cell(1, 4).Value = "Phone";
sheet.Cell(1, 5).Value = "Registered";
sheet.Cell(1, 6).Value = "Live Meeting";
var i = 1;
foreach(var attendee in evt.Attendees)
{
i++;
var profile = attendee.Profile;
sheet.Cell(i, 1).Value = profile.FirstName;
sheet.Cell(i, 2).Value = profile.LastName;
sheet.Cell(i, 3).Value = profile.Email;
sheet.Cell(i, 4).Value = profile.Phone;
sheet.Cell(i, 5).Value = att.Created.ToString();
sheet.Cell(i, 6).Value = att.LiveMeeting.ToString();
}
xls.Save();
}
Response.Clear();
Response.ContentType = "application/vnd.openxmlformats";
Response.AddHeader("Content-Disposition",
"attachment; filename=" + fileInfo.Name);
Response.WriteFile(fileInfo.FullName);
Response.Flush();
if (fileInfo.Exists)
fileInfo.Delete();
}
From the comments, I was able to verify that saving the file to a location on the disk and opening it directly did not open the file as read-only, implying the problem was not with the code, but the browsers handling of files downloaded from the internet.
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();
}