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");
Related
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())
{
...
}
I've been trying to get my ASP.NET MVC website to export some data as an Excel file. For hours I thought that NPOI was just producing garbage so I switched over to EPPlus. I tested it in LINQPad and it created a proper working XLSX file, so I moved the code over to the MVC app. AGAIN, I get corrupted files. By chance I happened to look at the temp directory and saw that the file created by EPPlus is 3.87KB and works perfectly, but the FileResult is returning a file that's 6.42KB, which is corrupted. Why is this happening? I read somewhere that it was the server GZip compression causing it, so I turned it off, and it had no effect. Someone, please help me, I'm going out of my mind... Here's my code.
[HttpGet]
public FileResult Excel(
CenturyLinkOrderExcelQueryModel query) {
var file = Manager.GetExcelFile(query); // FileInfo
return File(file.FullName, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", query.FileName);
}
As far as I'm concerned there's an issue with the FileResult and it's accompanying methods. I ended up "resolving" the issue by overriding the Response object:
[HttpGet]
public void Excel(
CenturyLinkOrderExcelQueryModel query) {
var file = Manager.GetExcelFile(query);
Response.Clear();
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("Content-Disposition", "attachment; filename=" + query.FileName);
Response.BinaryWrite(System.IO.File.ReadAllBytes(file.FullName));
Response.Flush();
Response.Close();
Response.End();
}
Try using the OpenRead from FileInfo to get a file stream and see if that works.
[HttpGet]
public FileResult Excel(CenturyLinkOrderExcelQueryModel query) {
var file = Manager.GetExcelFile(query); // FileInfo
var fileStream = file.OpenRead();
return File(fileStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", query.FileName);
}
I have a database table (tbl_document) which contains details of files which were uploaded into it over time.
I want to write a console application to download these files to a given location.
The table has three pieces of information, which I should use:
The FileContents in varbinary format;
The MIME Type (contentType) for each file, in nvarchar format for example something like this:
application/x-zip-compressed
application/msword
application/vnd.openxmlformats-officedocument.wordprocessingml.document
application/pdf
application/pdf
In MVC, I would have done something like this to perform this task:
public FileContentResult DownloadFile()
{
FileContentResult result = new FileContentResult(file.FileContents, file.ContentType);
result.FileDownloadName = file.FileName;
return result;
}
I have tried other ways to do this such as this one:
WebClient myWebClient = new WebClient();
FileContentResult result = new FileContentResult(fileContents, contentType);
I couldn't really get to save the file using any of the above. Please could you help.
Many thanks.
I have actually solved it like this:
// fileContents is the binary file being downloaded; I didn't need to use the MIME Types
MemoryStream ms = new MemoryStream(fileContents);
//write to file
FileStream file = new FileStream(filePath, FileMode.Create, FileAccess.Write);
ms.WriteTo(file);
file.Close();
ms.Close();
I have an MVC application that shows a list of files for download. The user selects the files with a checkbox and these are generated as PDF"s on the server side using the Pechkin library.
My problem is that when downloading more than one file, I'm getting the error that "Server cannot clear headers after HTTP headers have been sent." which I realise is because I can only send a single HTTP response back to the client, but I'm not sure how to go about fixing this.
public ActionResult Forms(FormsViewModel model)
{
foreach (var item in model.Forms)
{
if (item.Selected)
{
CreatePdfPechkin(RenderRazorViewToString(model.FormType, model.Form), model.Name);
}
}
return View(model);
}
private void CreatePdfPechkin(string html, string filename)
{
var pechkin = Factory.Create(new GlobalConfig());
var pdf = pechkin.Convert(new ObjectConfig()
.SetLoadImages(true).SetZoomFactor(1.1)
.SetPrintBackground(true)
.SetScreenMediaType(true)
.SetCreateExternalLinks(true), html);
Response.Clear();
Response.ClearContent();
// error on the next line for the second file to be downloaded
Response.ClearHeaders();
Response.ContentType = "application/pdf";
Response.AddHeader("Content-Disposition", string.Format("attachment;filename={0}.pdf; size={1}", filename, pdf.Length));
Response.BinaryWrite(pdf);
Response.Flush();
Response.End();
}
What's the pattern that I should be using to accomplish this?
You can zip all selected files and send it across. There is library available for same on codeplex called DotNetZip
This is how you can zip it.
var outputStream = new MemoryStream();
using (var zip = new ZipFile())
{
zip.AddFile("path to file one");
zip.AddFile("path to file two");
zip.AddFile("path to file three");
zip.AddFile("path to file four");
zip.Save(outputStream);
}
outputStream.Position = 0;
return File(outputStream, "application/zip","zip file name.zip");
I'm creating a PDF file on the fly using ITextSharp and ASP.NET 1.1. My process is as follows -
Create file on server
Redirect browser to newly created PDF
file so it is displayed to user
What I'd like to do is delete the PDF from the server as soon it is displayed in the users browser. The PDF file is large so it is not an option to hold it in memory, an initial write to the server is required. I'm currently using a solution that periodically polls for files then deletes them, but I'd prefer a solution that deletes the file immediately after it has been downloaded to the client machine. Is there a way to do this?
Instead of redirecting the browser to the created file you could serve the file yourself using you own HttpHandler. Then you could delete the file immediately after you served it or you could even create the file in memory.
Write the PDF file directly to the Client:
public class MyHandler : IHttpHandler {
public void ProcessRequest(System.Web.HttpContext context) {
context.Response.ContentType = "application/pdf";
// ...
PdfWriter.getInstance(document, context.Response.OutputStream);
// ...
or read an already generated file 'filename', serve the file, delete it:
context.Response.Buffer = false;
context.Response.BufferOutput = false;
context.Response.ContentType = "application/pdf";
Stream outstream = context.Response.OutputStream;
FileStream instream =
new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
byte[] buffer = new byte[BUFFER_SIZE];
int len;
while ((len = instream.Read(buffer, 0, BUFFER_SIZE)) > 0) {
outstream.Write(buffer, 0, len);
}
outstream.Flush();
instream.Close();
// served the file -> now delete it
File.Delete(filename);
I didn't try this code. This is just how I think it would work ...
Inspired by f3lix's answer (thanks f3lix!) I've come up with the folowing VB.net code -
HttpContext.Current.Response.ClearContent()
HttpContext.Current.Response.ClearHeaders()
HttpContext.Current.Response.ContentType = "application/pdf"
HttpContext.Current.Response.TransmitFile(PDFFileName)
HttpContext.Current.Response.Flush()
HttpContext.Current.Response.Close()
File.Delete(PDFFileName)
This appears to work - is the 'WriteFile' method I've used any less efficent that the stream methods used by f3lix? Is there a method available that's more efficient than either of our solutions?
EDIT (19/03/2009) Based on comments below I've changed 'WriteFile' method to 'TransmitFile' as it appears it sends the file down to client in chunks rather than writing the entire file to the webserver's memory before sending. Further info can be found here.
Or you could just return it to the browser without writing to disk at all:
byte[] pdf;
using (MemoryStream ms = new MemoryStream()) {
Document doc = new Document();
PdfWriter.GetInstance(doc, ms);
doc.AddTitle("Document Title");
doc.Open();
doc.Add(new Paragraph("My paragraph."));
doc.Close();
pdf = ms.GetBuffer();
}
Response.ContentType = "application/pdf";
Response.AddHeader("Content-Disposition", "attachment;filename=MyDocument.pdf");
Response.OutputStream.Write(pdf, 0, pdf.Length);
The solution:
Response.TransmitFile(PDFFileName)
Response.Flush()
Response.Close()
File.Delete(PDFFileName)
Simply doesn't work for me (file never makes it to client). Reading in a byte array and calling Response.BinaryWrite isn't an option either since the file may be large. Is the only hack for this to start an asynchronous process that waits for the file to be released and then delete it?