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!


One Response to “Changing the target filename of TransmitFile in asp.net mvc”

Leave a Reply