ClosedXml does not save file - c#

I have a web application that I want to export a datatable to Excel. I've done this in the code-behind of my pages, and now I'm trying to create a re-usable class in by app_code folder, but it doesn't seem to want to work the same way.
I have a function in my class:
using ClosedXML.Excel;
protected void ExportToExcel(DataTable dtWS)
{
string wbName = "MyWB";
var wb = new XLWorkbook();
var ws = wb.Worksheets.Add("MySheet");
(workbook is populated here, from the datatable)
wb.SaveAs("myfile.xlsx");
According to this site 1 and this site 2, this should work, but my code just sits there. It does not error out, and I think my syntax is correct up to the 'save as' line, but it does not export, or do anything.
I also tried this:
using (MemoryStream stream = new MemoryStream())
{
wb.SaveAs(stream);
return File(stream.ToArray(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "myFile");
}
Which I got from this site 3, but it give me the error
'System.IO.File' is a 'type' but is used like a 'variable'
(I confess, I don't know what that error means.)
This is the code that works in the code-behind page of a web file, but it doesn't work in my class object:
HttpResponse httpResponse = HttpContext.Current.Response;
httpResponse.Clear();
httpResponse.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
httpResponse.AddHeader("content-disposition", "attachment;filename=\"Caltrans_" + DateTime.Now.ToString("yyyy_MM_dd") + " .xlsx\"");
// Flush the workbook to the Response.OutputStream
using (MemoryStream memoryStream = new MemoryStream())
{
wb.SaveAs(memoryStream);
memoryStream.WriteTo(httpResponse.OutputStream);
memoryStream.Close();
}
httpResponse.End();
The upshot is, I think I'm creating and populating my workbook and worksheet just fine, but I cannot get them to export from a class in my app_code folder. Can anyone help?

Related

C# Generated Excel File: File Format or File Extension is not valid

I'm calling the action "Export" where i pass a list of viewmodels and define the format
public ActionResult DownloadTokenlist(string startDate = null, string endDate = null)
{
using (HRCTSStatisticDb db = new HRCTSStatisticDb(Setting.ClientId))
{
List<TokenExportViewModel> tokenExportViewModels = new List<TokenExportViewModel>();
Response.AddHeader("content-disposition", $"attachment;filename=Tokenlist_{DateTime.Now.ToString("dd.MM.yyyy")}.xlsx");
log.InfoFormat($"The {new HomeController().UserRole(Context.LoggedInUser)}: {Context.LoggedInUser} has used the exceldownload");
return File(new ExcelExport().Export(tokenExportViewModels), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
}
}
The action i call (ExcelEngine is by Syncfusion):
public MemoryStream Export(List<TokenExportViewModel> list)
{
MemoryStream stream = new MemoryStream();
using (ExcelEngine excelEngine = new ExcelEngine())
{
IApplication application = excelEngine.Excel;
application.DefaultVersion = ExcelVersion.Excel2010;
IWorkbook workbook = application.Workbooks.Create(1);
IWorksheet worksheet = workbook.Worksheets.Create("Tokenlist");
IStyle defaultStyle = workbook.Styles.Add("default");
defaultStyle.Font.Size = 12;
worksheet.SetDefaultColumnStyle(1, 20, defaultStyle);
worksheet.SetDefaultRowStyle(1, 300, defaultStyle);
worksheet.UsedRange.AutofitColumns();
worksheet.Range["A1"].Text = $"Tokenlist - {DateTime.Today.ToString("dd.MM.yyyy")}";
worksheet.Range["A1"].CellStyle = h1Style;
workbook.SaveAs(stream);
workbook.Close();
}
return stream;
}
I only posted the code which has an impact on the file and (maybe) could create the error.
There is no error, until i open the file, then this exception pops up:
Excel cannot open the file 'Tokenlist_22.05.2018.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.
I've tried to change the file format to .xls and .vbs but neither works. With .xls I can open the document but then it has no data in it.
The .close() doesn't change much, it just closes the output stream previously opened.
As stream reached end position while returning it, the downloaded file is getting corrupted. So, it is recommended to set its current position to 0 to resolve this issue. Please refer below code to achieve the same.
Code Example:
workbook.SaveAs(stream);
workbook.Close();
stream.Position = 0;
We have also shared a simple sample for your reference which can be downloaded from following link.
Sample Link: http://www.syncfusion.com/downloads/support/directtrac/general/ze/Sample1020485770.zip
I work for Syncfusion.
Use the FileContentResult Overload where you can provide thefileDownloadName like this:
return File(excelExport.Export(tokenExportViewModels).ToArray(),"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", $"Tokenlist_{DateTime.Now.ToString("dd.MM.yyyy")}.xlsx");
And use the stream ToArray() extension to return a byte[].
(I assume your Export method is generating a valid document)

WebAPI to download PowerPoint file using memory stream

I'm having trouble downloading a file with content using a Powerpoint presentation library (Syncfusion). The docs only supply ASP examples, no Web API specifically.
I can save a file to the file system which has the context I add to the Powerpoint.
I can get the API to download a file to the users browser but this Powerpoint is empty.
Syncfusion has a function to save the Powerpoint to a memory stream so I guess my question is what is the correct way to save a file to the users browser with the content from the stream?
I'm using HTTPGet and hitting the link through the browser. Do I need to sent the context-type or anything like that?
Thanks for your help, I can provide what I have so far if that helps.
Kurtis
Edit:
[HttpGet, Route("")]
public HttpResponseMessage Get()
{
var presentation = Presentation.Create();
var firstSlide = presentation.Slides.Add(SlideLayoutType.Blank);
var textShape = firstSlide.AddTextBox(100, 75, 756, 200);
var paragraph = textShape.TextBody.AddParagraph();
paragraph.HorizontalAlignment = HorizontalAlignmentType.Center;
var textPart = paragraph.AddTextPart("Kurtis' Presentation");
textPart.Font.FontSize = 80;
var memoryStream = new MemoryStream();
presentation.Save(memoryStream);
var result = Request.CreateResponse(HttpStatusCode.OK);
result.Content = new StreamContent(memoryStream);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "export.pptx"
};
return result;
}
This is what I have, the library saves the presentation to the memory stream, you can change the parameter to a string which writes this to a file and there is an option to pass a filename, format and HttpResponse for MVC but I couldn't get this working with my API controller. I kept getting a network error but didn't know why.
Thanks again
I found my problem with the help of others in the comments.
The code above work but I needed to reset the stream position to zero to actually write the data.
memoryStream.Position = 0;
Thanks for those who commented. :-)

EPPLus - export to Excel returns failed-network error

In my ASP.NET Core 1.1 app, I am exporting data to Excel using EPPLus.Core. But at the time of File download I get the error: Failed- network error
Controller
public async Task<FileStreamResult> CPT_RC_Excel(int FiscalYear)
{
var custlist = _Context.Customers.Where(c => c.Region=="NW").ToList();
MemoryStream ms = new MemoryStream();
using (ExcelPackage pck = new ExcelPackage(ms))
{
ExcelWorksheet ws = pck.Workbook.Worksheets.Add("Clients");
ws.Cells["A1"].LoadFromCollection(custlist);
Response.Clear();
Response.Headers.Add("content-disposition", "attachment; filename=TestFile.xlsx");
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
var bytes = pck.GetAsByteArray();
await Response.Body.WriteAsync(bytes, 0, bytes.Length);
return File(ms, "application/force-download", "TestFile.xlsx");
}
}
UPDATE:
Error occurs on both Google Chrome and IE 11. The entire app is on a local desktop. No network drive is involved here.
Error does seem to be related to the code above since another action method (code shown here) in the same controller does down load the csv/text files successfully.
You could try something like this:
public async Task<ActionResult> CPT_RC_Excel(int FiscalYear)
{
var fileContentAsByteArray = [generate your file as a byte array]
return File(fileContentAsByteArray, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "TestFile.xlsx");
}
if this doesn't work then the issue is with the file generation.
try removing the reference to the MemoryStream and use...
...
using (var excelPackage = new ExcelPackage())
{
...
}

Corrupt File when streaming from HTTP

I’m having an issue with streaming a file through the HTTP Response. No matter what I do, it comes out corrupted!
The background is that I need to send a generated XLS file (I'm using NPOI). I know that the generated file is fine, because if I save it directly to the disk with a FileStream, I can open it and there are no problems! However… when I try to stream that file through HTTP, it comes out corrupted (I’ve tried three different methods, shown below…). To add on top of that, it’s not only the XLS file that gets corrupted, it’s ALL files that I load (I’ve tried jpg, png, and txt files). Whenever I send them through HTTP, it gets corrupted.Anyways, here’s what I’ve tried:
I’ve tried manually constructing an HTTP response:
Export export = new Export(header, data);
MemoryStream stream = export.GetXLSStream("test"); // This generates a memory stream of the XLS file
// Writing that stream to a file works! This file opens just fine
var fs = new FileStream(#"C:\export.xls", FileMode.Create, System.IO.FileAccess.Write);
stream.WriteTo(fs);
// However, this doesn't!
Response.ClearContent();
Response.AddHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
Response.AddHeader("Content-Disposition", "attachment; filename=export.xls");
Response.AddHeader("Content-Type", "application/vnd.ms-excel");
Response.AddHeader("Content-Transfer-Encoding", "binary");
Response.BinaryWrite(stream.ToArray());
Response.End();
return null;
I’ve tried using the FileStreamResult:
Export export = new Export(header, data);
MemoryStream stream = export.GetXLSStream("test"); // This generates a memory stream of the XLS file
return File(stream, "application/vnd.ms-excel", "export.xsl");
I’ve tried using the FileContentResult:
Export export = new Export(header, data);
MemoryStream stream = export.GetXLSStream("test"); // This generates a memory stream of the XLS file
return File(stream.ToArray(), "application/vnd.ms-excel", "export.xsl");
I’ve tried using a FilePathResult:
Export export = new Export(header, data);
MemoryStream stream = export.GetXLSStream("test"); // This generates a memory stream of the XLS file
var fs = new FileStream(#"C:\export.xls", FileMode.Create, System.IO.FileAccess.Write);
stream.WriteTo(fs);
fs.Close();
return File(#"C:\export.xls", "application/vnd.ms-excel", "export.xsl");
And I’ve tried loading random files like:
return File(#"C:\test.jpg", "image/jpeg", "test.jpg");
Doing a MD5 or CRC check also shows me that the file I get through HTTP is not the same as the original file (even though they have the exact same amount of bytes).
Here is the code that works for me:
public MyController : Controller {
public ActionResult GetFile()
{
// ...
return File(stream, "application/octet-stream", "file.xls");
}
}
I do it in MVC with NPOI 1.2.5, .NET 4.0 in following way:
I have custom ActionResult class with following method:
public override sealed void ExecuteResult(ControllerContext context)
{
IWorkbook workbook = XlsData.CreateTestWorkbook().Workbook;
HttpResponseBase response = context.HttpContext.Response;
response.Clear();
response.ContentType = "application/vnd.ms-excel";
response.Headers.Add("content-disposition", "attachment; filename=Test.xls");
using (var ms = new MemoryStream())
{
workbook.Write(ms);
ms.WriteTo(response.OutputStream);
}
response.End();
}
where XlsData.CreateTestWorkbook() is some class which creates me NPOI sample workbook.
Then in my Controller method I simply return my custom ActionResult
My controller method has a [HttpGet] attribute and is called from
client side with a html link download button.
Looks like it was a Whitespace filter I had applied at the controller's root to make the output all a single line. It shouldn't have been applied to anything but an html response, but I've changed the code so that it doesn't run at all for response like this.
Try this method, it works fine for me
using (var fileData = new FileStream(filename, FileMode.Create))
{
workbook.Write(fileData);
}
using (var exportData = new MemoryStream())
{
workbook.Write(exportData);
string saveAsFileName = string.Format("MembershipExport-{0:d}.xls", DateTime.Now).Replace("/", "-");
Response.ContentType = "application/vnd.ms-excel";
Response.AddHeader("Content-Disposition", string.Format("attachment;filename={0}", saveAsFileName));
Response.Clear();
Response.BinaryWrite(exportData.GetBuffer());
Response.End();
}
MemoryStream outStream = (MemoryStream)generateFilte.Generate();
outStream.Flush(); //Always catches me out
outStream.Position = 0;
return new FileStreamResult(outStream, "application/vnd.ms-excel");

put generated pdf file without saving it on the server

I have code (in a .ashx-file) that generates a PDF file from a PDF template. The generated pdf gets personalized with a name and a code. I use iTextSharp to do so.
This is the code:
using (var existingFileStream = new FileStream(fileNameExisting, FileMode.Open))
using (var newFileStream = new FileStream(fileNameNew, FileMode.Create))
{
var pdfReader = new PdfReader(existingFileStream);
var stamper = new PdfStamper(pdfReader, newFileStream);
var form = stamper.AcroFields;
var fieldKeys = form.Fields.Keys;
form.SetField("Name", name);
form.SetField("Code", code);
stamper.FormFlattening = true;
stamper.Close();
pdfReader.Close();
}
context.Response.AppendHeader("content-disposition", "inline; filename=zenith_coupon.pdf");
context.Response.TransmitFile(fileNameNew);
context.Response.ContentType = "application/pdf";
This works, but it saves the file on the server. I don't want to do that because there're going to be a lot of people downloading the PDF file and the server will be full in no time.
So my question is, how can I generate a PDF with iTextSharp without saving it and put it to the user?
Instead of using a FileStream you could use a MemoryStream and then use Response.Write() to output the stream contents.
You can use any Stream (for example MemoryStream) for the intermediate PDF (in your code currently named newFileStream) if you don't want to save it as a file - for sample code see http://www.developerfusion.com/code/6623/dynamically-generating-pdfs-in-net/ and http://forums.asp.net/t/1093198.aspx/1.
Just remember to rewind (i.e. set Position = 0) the MemoryStream before transmitting it to the client (for example by Response.Write or CopyTo (Response.OutputStream) )...

Categories