有没有一种干净的方法来返回 FilePathResult 并随后删除磁盘上的文件?

gre*_*mac 5 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

控制器代码创建并操作该文件,然后将结果返回为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
            };
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

(请注意,GeneratorSigner正在通过依赖注入插入到控制器中,并保持我不想依赖的关注SetupFileGenerator点分离DigitalSignatureTool。重要的是,我确实需要磁盘上的文件Signer.Sign来运行 - 因此Generator.Generate()可以不只是返回一个 Stream)。

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


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

  • 实现一个新类,该类派生自该FilePathResult文件,但也会在发送后删除该文件
  • 重构我的代码,使其不再SetupFile具有属性string LocalFilePath,而是具有MemoryStream FileContents

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

phi*_*lip 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);
    } 
} 
Run Code Online (Sandbox Code Playgroud)

只需按如下方式装饰您的方法:

[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
        };
    }
}
Run Code Online (Sandbox Code Playgroud)