Posts Tagged ‘output response’

Say a file has a physical filename of ‘xyz.pdf’, but when you send it to the user you want it to have a name like ‘myxyzbrilliantfilename.pdf’. How do you change the target filename when transmitting files through the output stream of Response without it popping up a download dialog?

I have searched wide and wild and the only way you could do this is by modifying the “ContentDisposition” like so:

Response.AddHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
// OR
Response.AddHeader("Content-Disposition", "inline; filename=\"" + fileName + "\"");

The “attachment” setting opens a dialog to save the file with the correct name and the “inline” setting doesnt open a dialog, but ignores the filename!
This is not the behaviour I am looking for, so I had a quick coffee and came up with the following very simple solution!

My solution came in the form of a new route in my mappings:

routes.MapRoute(
    "ResourceModulePdf", // Route name
    "ResourceModule/Pdf/{id}.pdf", // URL with parameters
    new
    {
        controller = "ResourceModule",
        action = "Pdf",
        id = "",
        sourceFilename = ""
    } // Parameter defaults
    );

and my ‘pdf’ action:

public ActionResult Pdf(string id, string sourceFilename)
{
    // We do not need to do anything with 'id'. It has served it's purpose already
    return new FileResult(sourceFilename, "application/pdf");
}

… and my FileResult

public class FileResult : ActionResult
{
    public String ContentType { get; set; }
    public byte[] ImageBytes { get; set; }
    public String SourceFilename { get; set; }

    //This is used for times where you have a physical location
    public FileResult(String sourceFilename, String contentType)
    {
        SourceFilename = sourceFilename;
        ContentType = contentType;
    }

    //This is used for when you have the actual image in byte form
    //  which is more important for this post.
    public FileResult(byte[] sourceStream, String contentType)
    {
        ImageBytes = sourceStream;
        ContentType = contentType;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        var response = context.HttpContext.Response;
        response.Clear();
        response.Cache.SetCacheability(HttpCacheability.NoCache);
        response.ContentType = ContentType;

        if (ImageBytes != null)
        {
            var stream = new MemoryStream(ImageBytes);
            stream.WriteTo(response.OutputStream);
            stream.Dispose();
        }
        else
            response.TransmitFile(SourceFilename);
    }
}

All we need to do now is construct our hyperlinks like so:


Doc

By default the “TransmitFile” method will take the name of the filename you used in your hyperlink and the result is exactly what we wanted!