I am currently writing an ASP.NET MVC 5 controller action to export some data to an Excel file (using some code I found here). It works...mostly. It outputs an Excel file, which I can open, but not before displaying the following error message:
"The file format and extension of 'Export.xls' don't match. The file could be corrupted or unsafe. Unless you trust it's source, don't open it. Do you want to open it anyway?"
I go on to select "Yes" and then it opens. I can resave it on my machine and open that file and I don't get the error. It otherwise works fine, the only other oddness is that the file's gridlines are formatted differently than is usual for an Excel file, almost more like an HTML table than an Excel sheet. However, the weird error message isn't something that would be acceptable here, so I have to fix it.
Here is my code:
public void ExportExcel()
{
// DataObject is a class that fetches the data for this method
DataObject dataObj = new DataObject();
var grid = new GridView();
// dataObj.GetDataList returns a List<T> of data model class objects
grid.DataSource = dataObj.GetDataList();
grid.DataBind();
Response.ClearContent();
Response.Buffer = true;
Response.AddHeader("content-disposition", "attachment; filename=Export.xls");
Response.ContentType = "application/vnd.ms-excel";
Response.Charset = "";
StringWriter sw = new StringWriter();
HtmlTextWriter htw = new HtmlTextWriter(sw);
grid.RenderControl(htw);
byte[] byteArray = Encoding.ASCII.GetBytes(sw.ToString());
MemoryStream s = new MemoryStream(byteArray);
StreamReader sr = new StreamReader(s, Encoding.ASCII);
Response.Write(sr.ReadToEnd());
Response.End();
}
I have already tried setting the Response.ContentType to other values ("application/excel", "application/ms-excel"), to no avail. I'm a little new to ASP.NET and C# in general, so there might be something I'm missing or doing wrong here; I'm more used to PHP and Zend. Any insight or help you could give would be greatly appreciated; thanks!
You're writing an HTML table as an Excel file. Basically, you're taking text with this:
<table>
<tr>
<td>
Stuff
</td>
</tr>
</table>
and writing it as a text file with a .xls extension. Excel is "smart" enough (if you can call it that) to open the file and display it properly, although it alerts you that the file isn't actually an xls file first.
You need to either deal with it (not a good solution), convert the data in the table to a csv and send a CSV (a much better solution) or use an Excel library to create an actual Excel file and send that. Of those, the CSV is probably the easiest.
i got the same error. So, I started (using Microsoft.Office.Interop.Excel) get it from Nuget Manager.
Here is the code
Microsoft.Office.Interop.Excel.Application xla = new Microsoft.Office.Interop.Excel.Application();
Workbook wb = xla.Workbooks.Add(XlSheetType.xlWorksheet);
Worksheet ws = (Worksheet)xla.ActiveSheet;
/*Headers here */
ws.Cells[1, 1] = "Header1";
ws.Cells[1, 2] = "Header2"; ws.Cells[1, 3] = "Header3"; ws.Cells[1, 4] = "Header4"; ws.Cells[1, 5] = "Header5";
int i = 2;
/* use your list here to fill the data rows */
foreach (var a in pavmm.Funds)
{
ws.Cells[i, 1] = a.FilingID;
ws.Cells[i, 2] = a.Security_Name; ws.Cells[i, 3] = a.Filing_Type; ws.Cells[i, 4] = a.st_name; ws.Cells[i, 5] = a.Permit;
i = i + 1;
}
ws.Columns.AutoFit();
ws.Rows.AutoFit();
string folder = NewFolderName + "\\";
if (!Directory.Exists(folder))
Directory.CreateDirectory(folder);
string filename = ExcelsheetName + ".xlsx";
filename = filename.Replace('/', '-');
filename = filename.Replace('\\', '-');
string path = Path.Combine(folder, filename);
wb.SaveCopyAs(path);
Related
In my razor page application, I have a button that you click which creates an excel file and should automatically save it to your downloads folder.
The code below works great in localhost - I click the button, it saves to MY downloads folder, and I can view it.
However, once I publish and try, I receive an error that states "Could not find a part of the path 'C:\WINDOWS\system32\config\systemprofile\Downloads\PartCommentHistory.xlsx'.".
I would also be perfectly fine changing this code to instead pull up the save file dialog window and allow the user to pick where the file gets saved in the first place - but I'm not sure how. Google isn't helping much, so here we are!
If I physically navigate to this path, I noticed that there is no Downloads folder. I tried adding an if statement in my code that says if the Downloads folder doesn't exist here, create it first and then save the file there. However, that produces another error which is that I don't have access to the path.
public async Task<IActionResult> OnPostExportAsync(string currentFilter)
{
string sFilePath = Path.Combine(Environment.ExpandEnvironmentVariables("%USERPROFILE%"),"Downloads");
string sFileName = #"PartCommentHistory.xlsx";
string URL = string.Format("{0}://{1}/{2}", Request.Scheme, Request.Host, sFileName);
FileInfo file = new FileInfo(Path.Combine(sFilePath, sFileName));
var memory = new MemoryStream();
using (var fs = new FileStream(Path.Combine(sFilePath, sFileName), FileMode.Create, FileAccess.Write))
{
ExcelPackage pck = new ExcelPackage();
ExcelWorksheet ws = pck.Workbook.Worksheets.Add("Worksheet1");
List<CmtPartComment> commentlist = _context.CmtPartComments.Select(x => new CmtPartComment
{
SupplierNo = x.SupplierNo,
PartNo = x.PartNo,
Comment = x.Comment,
EnterBy = x.EnterBy,
EnteredDt = x.EnterDt.ToString("yyyy-MM-dd HH:mm:ss tt"),
CompletedDt = x.CompleteDt.ToString("yyyy-MM-dd HH:mm:ss tt")
}).Include(c => c.System).OrderByDescending(x => x.EnterDt).Where(x => x.PartNo == currentFilter).ToList();
ws.Cells[1, 1].Value = "SupplierNo";
ws.Cells[1, 2].Value = "PartNo";
ws.Cells[1, 3].Value = "Comment";
ws.Cells[1, 4].Value = "EnterBy";
ws.Cells[1, 5].Value = "EnterDt";
ws.Cells[1, 6].Value = "CompleteDt";
int recordIndex = 2;
foreach (var item in commentlist)
{
ws.Cells[recordIndex, 1].Value = item.SupplierNo;
ws.Cells[recordIndex, 2].Value = item.PartNo;
ws.Cells[recordIndex, 3].Value = item.Comment;
ws.Cells[recordIndex, 4].Value = item.EnterBy;
ws.Cells[recordIndex, 5].Value = item.EnteredDt;
ws.Cells[recordIndex, 6].Value = item.CompletedDt;
recordIndex++;
}
ws.Cells["A:AZ"].AutoFitColumns();
pck.SaveAs(fs);
}
using (var stream = new FileStream(Path.Combine(sFilePath, sFileName), FileMode.Open))
{
await stream.CopyToAsync(memory);
}
memory.Position = 0;
return File(memory, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", sFileName);
}
For your issue, it is caused by that you are creating a temp file in the server side by using (var fs = new FileStream(Path.Combine(sFilePath, sFileName), FileMode.Create, FileAccess.Write)) which may not exist in the server side.
For your requirement, you are trying to create a file and return it to client side. If so, there is no need to create the local file in the server side, you could return the byte of the file like below:
public async Task<IActionResult> OnPostExportByInMemoryAsync(string currentFilter)
{
string sFileName = #"PartCommentHistory.xlsx";
using (var pck = new ExcelPackage())
{
ExcelWorksheet ws = pck.Workbook.Worksheets.Add("Worksheet1");
ws.Cells[1, 1].Value = "SupplierNo";
ws.Cells[1, 2].Value = "PartNo";
ws.Cells[1, 3].Value = "Comment";
ws.Cells[1, 4].Value = "EnterBy";
ws.Cells[1, 5].Value = "EnterDt";
ws.Cells[1, 6].Value = "CompleteDt";
ws.Cells["A:AZ"].AutoFitColumns();
return File(pck.GetAsByteArray(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", sFileName);
}
}
Use this method to get a folder path
Environment.GetFolderPath(Environment.SpecialFolder.Yourspecialfoldernamehere, System.Environment.SpecialFolderOption.None)
For example
Environment.GetFolderPath(Environment.SpecialFolder.System));
In above example System is a special folder.
You can't determine where on the client machine the file can be saved. The only reason it appears to work on your machine is because your machine acts as the server. All you can do is to force a Save or Open dialog when the user downloads the file, which is achieved by setting the content type to application/octet-stream:
Do I need Content-Type: application/octet-stream for file download?
I am creating an Excel file using the EPPlus library. When I create file and open up the file, the following pop up message shows:
We found a problem with some content in 'ExcelDemo.xlsx'. Do you want us to try to recover as much as we can? If you trust the source of this workbook, Click Yes
I am using following code
using (ExcelPackage pck = new ExcelPackage())
{
//Create the worksheet
ExcelWorksheet ws = pck.Workbook.Worksheets.Add("Demo");
//Load the datatable into the sheet, starting from cell A1. Print the column names on row 1
ws.Cells[1, 2].Value = "Excel Download";
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("content-disposition", "attachment; filename=ExcelDemo.xlsx");
Response.BinaryWrite(pck.GetAsByteArray());
}
Is there problem in my code or is this an Excel issue?
In my case the problem was in calling
package.Save();
and using
Response.BinaryWrite(package.GetAsByteArray());
at the same time.
When you call package.GetAsByteArray() it perfoms following operations internally:
this.Workbook.Save();
this._package.Close();
this._package.Save(this._stream);
So, calling package.Save two times leads to this error when opening in Excel.
At the start, you need to add in a:
Response.Clear();
Then at the end add a
Response.End();
I will share my solution. I am using a template excel file and then create new excel from it.
Receiving the same error. My code was
using (Stream newFileStream = File.Open(this.tempFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite))
using (Stream originalFile = File.Open(this.initialFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (ExcelPackage excelPackage = new ExcelPackage(newFile, template))
{
// ... Do work here
}
I had to change code to:
FileInfo intialInfo = new FileInfo(this.initialFilePath);
FileInfo tempFileInfo = new FileInfo(this.tempFilePath);
using (ExcelPackage excelPackage = new ExcelPackage(tempFileInfo, intialInfo))
{
//... Do work here
}
Also I am using ASP MVC and the response is:
byte[] result = exporter.GetBytesFromGeneratedExcel();
return this.File(result, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "Test.xlsx");
In my case, problem was data table name which I did not set earlier.
Try this one,
dt.TableName = "ExcelSheet1";
I was having this problem because I was writing a string larger than 32,767 characters into a single cell. Excel doesn't like this, but EPPlus won't stop you from doing it.
My code was updated... and was getting the same error.... and I finally found my solution
Original Code:
public static void ExportToExcel(HttpContext ctx, DataTable tbl, string fileName)
{
try
{
using (ExcelPackage pck = new ExcelPackage())
{
//Create the worksheet
ExcelWorksheet ws = pck.Workbook.Worksheets.Add(fileName);
//Load the datatable into the sheet, starting from cell A1. Print the column names on row 1
ws.Cells["A1"].LoadFromDataTable(tbl, true);
int rowCount = tbl.Rows.Count;
List<int> dateColumns = new List<int>();
foreach (DataColumn d in tbl.Columns)
{
if (d.DataType == typeof(DateTime))
dateColumns.Add(d.Ordinal + 1);
}
CultureInfo info = new CultureInfo(ctx.Session["Language"].ToString());
foreach (int dc in dateColumns)
ws.Cells[2, dc, rowCount + 1, dc].Style.Numberformat.Format = info.DateTimeFormat.ShortDatePattern;
//Write it back to the client
ctx.Response.Clear();
ctx.Response.AddHeader("content-disposition", "attachment; filename=" + fileName + ".xlsx");
ctx.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
ctx.Response.Buffer = false;
ctx.Response.BufferOutput = false;
ctx.Response.BinaryWrite(pck.GetAsByteArray());
ctx.Response.End();
}
}
catch (Exception EX)
{
ctx.Response.Write(EX.ToString());
}
}
Code update:
catch (Exception EX)
{
if (!(EX is System.Threading.ThreadAbortException))
{
ctx.Response.Write(EX.ToString());
}
}
IT WORKED!
A solution I came up with was just returning the file object in the controller. The first argument should be the byte array (file), the second argument is the content type, and the last argument is the filename (in my case "report.xlsx").
return File(file, "application/octet-stream", fileName);
Hope this helped.
i have develop an application for online store in which different store keep there catalog online. but i have to develop an functionality for download there catalog in xls file for that i have my data in datatable which i have to write in dynamically generated xls file and download it.
for that i have try fallowing :
DataTable ProductDetails = sql.ExecuteSelectCommand("SELECT * FROM Products_Details_View WHERE Supp_Id = " + Session["SuppID"].ToString() + " and Is_Available = 1");
Response.ClearContent();
Response.AddHeader("content-disposition", "attachment;filename=Catalog.xls");
Response.ContentType = "application/excel";
Response.Write(ProductDetails);
Response.End();
i refer it here
but am not getting any thing
please help to get out of it.
I use the EPPlus package, which you can install via Nuget. It allows you to load data onto an Excel worksheet directly from your datatable, and it includes support for things like formatting on the worksheet (fonts, column widths etc). See their
documentation page here on using it inside a web application.
For your case, I would suggest something like:
DataTable ProductDetails = sql.ExecuteSelectCommand("SELECT * FROM Products_Details_View WHERE Supp_Id = " + Session["SuppID"].ToString() + " and Is_Available = 1");
using (ExcelPackage pck = new ExcelPackage())
{
//Create the worksheet
ExcelWorksheet ws = pck.Workbook.Worksheets.Add("Demo");
//Load the datatable into the sheet, starting from cell A1.
//Print the column names on row 1
ws.Cells["A1"].LoadFromDataTable(ProductDetails, true);
//Write it back to the client
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("content-disposition", "attachment; filename=ProductDetails.xlsx");
Response.BinaryWrite(pck.GetAsByteArray());
}
Try this
string attachment = "attachment; filename=xxxx" + DateTime.Now + ".xls";
Response.ClearContent();
Response.AddHeader("content-disposition", attachment);
Response.ContentType = "application/ms-excel";
StringWriter stw = new StringWriter();
HtmlTextWriter htextw = new HtmlTextWriter(stw);
ProductDetails.RenderControl(htextw);
GridView dg = new GridView(); //Create an empty Gridview to bind to datatable.
dg.AutoGenerateColumns = true;
dg.DataSource = ProductDetails;
dg.DataBind();
dg.RenderControl(htw);
Response.Write(stw.ToString());
stw.Close();
Response.End();
I am creating excel and filling values into it. i am using EPPlus.
I can save it using filestream and open it.
but i want to open it without saving.
I thought we can use memorystream in some way to generate the excel directly.
Please guide.
try
{
MemoryStream newFile = new MemoryStream();
using (ExcelPackage package = new ExcelPackage(newFile))
{
// Add Data Collection worksheet
ExcelWorksheet dataWorksheet = package.Workbook.Worksheets.Add("Sheet1");
dataWorksheet.Cells[1, 1].Value = " My Text";
dataWorksheet.Cells[1, 1].Style.Font.Size = 14;
dataWorksheet.Cells[3, 1].Value = BALGlobalVariables.cocName;
dataWorksheet.Cells[5, 1].Value = "IR From Date :";
dataWorksheet.Cells[6, 1].Value = "IR To Date : ";
dataWorksheet.Cells[5, 6].Value = "From Date :";
dataWorksheet.Cells[6, 6].Value = "To Date : ";
dataWorksheet.Cells[5, 2].Value = fromDate;
dataWorksheet.Cells[6, 2].Value = toDate;
dataWorksheet.Cells[5, 7].Value = invFromDate;
dataWorksheet.Cells[6, 7].Value = invFromDate;
// Template specific excel generation goes in here
FillPurchaseExcelData(ref dataWorksheet, masterTable, subTable);
// save package
package.Save();
}
byte[] fileContent = newFile.ToArray();
#if DEBUG
string tempName = "MTemp.xlsx";
string tempFileName = System.IO.Path.GetDirectoryName(Environment.GetCommandLineArgs()[0]) + #"\" + tempName;
//Write the stream data of workbook to the root directory
using (FileStream file = new FileStream(tempFileName, FileMode.Create))
{
file.Write(fileContent, 0, fileContent.Length);
}
System.Diagnostics.Process.Start(tempFileName);
You can generate excel file and choose not to save it, but Excel can't open files that are not saved on disk (it becomes a 'file' when it's on disk), so in short - you have to save document in order to open it in Excel.
If you're concerned about storing data on hard drive, as an alternative you could create a data source on a medium you trust, and then create an xslx that would instruct Excel to consume that data source when opening worksheet (instead of creating a file pre-filled with data). But this is a whole other story...
I am using VS2005 c#.
I have this function for converting pipe delimited text files into excel format.
The files will then be stored in the C:\ of the user PC.
However, when I deployed my web application into the test server, the files are saved in the C:\ of the test server instead.
Below is my code snippet, may I know how can I change it to be saved on the users' pc instead of the server's, or if possible, show a popup message for the user to open/save the file. Thank you
if (TextFile.HasFile)
{
string strFileName = Server.HtmlEncode(TextFile.FileName);
string strExtension = Path.GetExtension(strFileName);
string activeDir = #"C:\";
string newPath = System.IO.Path.Combine(activeDir, "App_Converted");
System.IO.Directory.CreateDirectory(newPath);
string strExcelOutputFilename = "C:/App_Converted/" + xlExtension;
using (StreamWriter outputWriter = new StreamWriter(File.Create(strExcelOutputFilename)))
{
StreamReader inputReader = new StreamReader(TextFile.FileContent);
while (inputReader.Peek() >= 0)
{
string[] myInputFields = inputReader.ReadLine().Split(new char[] { '|' });
List<string> myOutputFields = new List<string>();
foreach (string aField in myInputFields)
{
string oField = aField;
if (oField.Contains(","))
oField = "\"" + oField + "\"";
myOutputFields.Add(oField);
}
outputWriter.WriteLine(string.Join(",", myOutputFields.ToArray()));
}
inputReader.Close();
UploadStatusLabel.Text = "File stored at [C:/App_Converted/] as file name [namelist.csv]";
Response.Clear();
Response.ContentType = "application/vnd.xls";
Response.AddHeader("Content-Disposition", "attachment;filename=namelist.csv");
StringWriter swriter = new StringWriter();
HtmlTextWriter hwriter = new HtmlTextWriter(swriter);
Response.Write(swriter.ToString());
Response.End();
return;
}
EDIT:
Updated code with save windows. However, I do not know how to link my converted file to the save windows. Need some help here. Above is my updated code.
instead of:
Response.ContentType = "application/vnd.xls";
Response.AddHeader("Content-Disposition", "attachment;filename=namelist.csv");
StringWriter swriter = new StringWriter();
HtmlTextWriter hwriter = new HtmlTextWriter(swriter);
Response.Write(swriter.ToString());
use this snippet
Response.ContentType = "application/vnd.xls";
Response.AddHeader("Content-Disposition", "attachment;filename=namelist.csv");
string filePath = #"paste a path of your file here";
Response.WriteFile(filePath);
p.s.
and please read the so question that I have posted in the comment. It exactly answers your question and covers many aspects of how to force a download of file from server
You cannot save file to the client machine with the above method.
You can either store it in server as you do here and provide (href) link to the client.
Or you can write the file as a response back to the browser which will either open it or give options to save. use HttpResponse.TransmitFile