Question

I am developing a web site using .NET MVC3. I have a controller where I make a file download to the client.

    [DeleteFileAfterDownloadFilter()]
    public FileResult DownloadVersion(int VersionID)
    { 
        //staff to get the tempZipFile    
        return File(tempZipFile, "zip", "file.zip"); 
    }

what I like to do is to delete the file after downloading this file. I figured that I can use ActionFilterAttribute. So I wrote the class below:

public class DeleteFileAfterDownloadFilter : ActionFilterAttribute
{
    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
        string fileName = ((FileStreamResult)filterContext.Result).FileDownloadName;
        File.Delete(fileName);
        base.OnResultExecuted(filterContext);
    }
}

I guess I have 2 problems here. First one is that when I run this thing it gives me the error of ".../Company/DownloadVersion?versionID=2057" page could not be found. What is the way to make it work?

And the second problem is that as you might have realized "((FileStreamResult)filterContext.Result).FileDownloadName" is probably not the filePath that I want to delete. It should be the "tempZipFile" local variable in controller. But I don't know how to pass that value to this event.

Was it helpful?

Solution

I gave your filter a spin and (after corrections) it produces a nasty COM error.

This is because the async nature of the the operation: OnResultExecuted is your last chance to do something but this happens when the response (with the filename but not the file itself) has been send back to the client. When the client (browser) then starts the download a Not Found error or worse is produced.

In other words, your approach looks nice but it won't work.

Some rough ideas for a solution:

  • make sure your server side files have unique names and a reliable timestamp
  • use a background process to periodically clean them up, or
  • clean up old file every time you prepare a new one

I changed your filter like this:

    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {            
        base.OnResultExecuted(filterContext);

        var r = filterContext.Result as FilePathResult; // not FileContent           
        string fileName =
           filterContext.RequestContext.HttpContext.Server.MapPath(r.FileName);            
        System.IO.File.Delete(fileName);
    }

Update :

Thanks to this SO answer, the following should work:

    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {            
        base.OnResultExecuted(filterContext);

        var r = filterContext.Result as FilePathResult; // not FileContent           
        string fileName =
           filterContext.RequestContext.HttpContext.Server.MapPath(r.FileName);   

        filterContext.HttpContext.Response.Flush();
        filterContext.HttpContext.Response.End();

        System.IO.File.Delete(fileName);
    }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top