views:

44

answers:

2

I have a controller action declared as follows:

[Authorize(Order = 0, Roles = "Requester,Controller,Installer")]
public FileStreamResult ExportJobCards()

The body of this method builds a collection of CSV lines, and attempts to return them as a file as follows:

using (var sw = new StreamWriter(new MemoryStream()))
{
    foreach (var line in lines)
    {
        sw.WriteLine(line);
    }
    return new FileStreamResult(sw.BaseStream, "text/csv");
}  

When I request this action using the following action link...

Html.ActionLink("Export to Excel", "ExportJobCards")

...the export method executes properly, i.e. all the required CSV data is present in the lines collection in the above code, but I get a File Not Found error rendered as the end result.

EDIT: In agreement with Tommy's observation, I moved the return out of the using, and I now get a file, but the file is empty. The new code that actually produces a file, ableit empty, is:

var sw = new StreamWriter(new MemoryStream());            
foreach (var line in lines)
{
    sw.WriteLine(line);
}
sw.Flush();
return new FileStreamResult(sw.BaseStream, "text/csv");  
A: 

It throws that error because you're not giving it a file stream. What you want is the FileContentResult into which you can pass arbitrary content. This content needs to be a byte array of your content, probably easiest to:

  1. use a stringbuilder rather than a streamwriter
  2. get your string from the builder
  3. use the static method System.Text.UnicodeEncoding.Unicode.GetBytes(string) to get the byte array
  4. Give the byte array to FileContentResult

As you have to write this code anyway the easiest thing to do would be to create a new FileStringResult that inherits from the base FileResult that can take in a string or stringbuilder. Override WriteFile(HttpResponseBase response) to do the string to byte[] conversion and push that into the response. Take a look at the FileStreamResult class from the MVC sources, it's very small and easy to do.

Chao
+1  A: 

With your current setup, the Using statement is disposing of the StringWriter before the return can complete, which is resulting in the null reference/file not found error. Remove the using statement or set the StringWriter to another variable before you exit out and you should be good to go on getting rid of the File Not Found error.

A thought on your second issue now, looking into memorystreams as filestream results, you may need to change your return to this

sw.BaseStream.seek(0, SeekOrigin.Begin)
return new FileStreamResult(sw.BaseStream, "text/csv"); 

as the pointer is still at the end of the stream when you return.

Tommy
Right on both counts, thanks. Wish I could give you 2 votes.
ProfK