是否有一种干净的方法来返回FilePathResult并在之后删除磁盘文件?

时间:2015-02-23 20:42:20

标签: c# asp.net asp.net-mvc

我有一个使用外部进程创建文件的类,并将其作为下载结果返回,此时我想从服务器中删除该文件。我通常喜欢避免临时的磁盘文件,但在这种情况下,它是不可避免的。


我最初尝试使用Dispose方法实现它:

public class SetupFile : IDisposable 
{
    /// <summary>Local file path</summary>
    public string LocalFilePath { get; set; }
    /// <summary>Filename to present to user</summary>
    public string DownloadFilename { get; set; }

    public void Dispose()
    {
        System.IO.File.Delete(LocalFile);
    }
}

控制器代码创建并操作此文件,然后将结果作为FilePathResult

返回
public class DownloadController : Controller 
{
    public SetupFileGenerator Generator { get; set; } 
    public DigitalSignatureTool Signer { get; set; }

    [HttpPost, Route("/download")]
    public ActionResult Download(InstallParams params) 
    {
        using (SetupFile file = Generator.Generate(params)) {
        {   
            Signer.Sign(file.LocalFilePath); // note: requires a local path!

            return new FilePathResult(file.LocalFilePath, "application/octet-stream")
            {
                FileDownloadName = file.DownloadFilename
            };
        }
    }
}

(请注意,GeneratorSigner正通过依赖注入插入到Controller中,为了保持关注点的分离,我不希望SetupFileGenerator依赖DigitalSignatureTool 1}}。关于这一点的重要一点是,我确实需要磁盘上的文件才能运行Signer.Sign - 因此Generator.Generate()无法返回流。)

这里的问题是FilePathResult仅在稍后在处理管道中调用its WriteFile() method时发送文件,这意味着我的SetupFile.Dispose()方法已被调用。


我认为我的下一步是做两件事之一:

  • 实施一个派生自FilePathResult的新类,但在发送后删除该文件
  • 重构我的代码,以便代替SetupFile拥有属性string LocalFilePath,而MemoryStream FileContents

然而,这似乎是一个相当常见的模式,所以在我重新发明轮子之前,是否有最佳实施方法?有什么需要特别注意的吗?

1 个答案:

答案 0 :(得分:3)

OnResultExecuted方法在写入响应后运行。您可以使用ActionFilterAttribute覆盖它。

public class DeleteFileAttribute : ActionFilterAttribute 
{ 
    public override void OnResultExecuted(ResultExecutedContext filterContext) 
    { 
        filterContext.HttpContext.Response.Flush();
        string filePath = (filterContext.Result as FilePathResult).FileName;
        System.IO.File.Delete(filePath);
    } 
} 

按照以下方式装饰您的方法:

[DeleteFile]
[HttpPost, Route("/download")]
public ActionResult Download(InstallParams params) 
{
    using (SetupFile file = Generator.Generate(params))
    {   
        Signer.Sign(file.LocalFilePath);

        return new FilePathResult(file.LocalFilePath, "application/octet-stream")
        {
            FileDownloadName = file.DownloadFilename
        };
    }
}
相关问题