Question

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:

  1. 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;
    
  2. 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");
    
  3. 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");
    
  4. 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");
    
  5. 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).

Was it helpful?

Solution 3

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.

OTHER TIPS

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.

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");
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top