【发布时间】:2011-01-03 17:52:45
【问题描述】:
我想在下载后立即删除文件,我该怎么做?我尝试继承 FilePathResult 并覆盖 WriteFile 方法,在该方法中我删除文件后
HttpResponseBase.TransmitFile
被调用,但这会挂起应用程序。
我可以在用户下载文件后安全地删除它吗?
【问题讨论】:
-
解决方案应该出现在“答案”中,而不是问题中。
标签: c# asp.net-mvc
我想在下载后立即删除文件,我该怎么做?我尝试继承 FilePathResult 并覆盖 WriteFile 方法,在该方法中我删除文件后
HttpResponseBase.TransmitFile
被调用,但这会挂起应用程序。
我可以在用户下载文件后安全地删除它吗?
【问题讨论】:
标签: c# asp.net-mvc
您可以只返回以FileOptions.DeleteOnClose 打开的普通FileStreamResult。文件流将由 asp.net 与结果一起处理。这个答案不需要使用在某些情况下可能适得其反的低级响应方法。也不会做额外的工作,比如将文件整体加载到内存中。
var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.None, 4096, FileOptions.DeleteOnClose);
return File(
fileStream: fs,
contentType: System.Net.Mime.MediaTypeNames.Application.Octet,
fileDownloadName: "File.abc");
此答案基于 Alan West 的回答和 Thariq Nugrohotomo 的评论。
【讨论】:
读取文件的字节,删除它,调用基本控制器的文件操作。
public class MyBaseController : Controller
{
protected FileContentResult TemporaryFile(string fileName, string contentType, string fileDownloadName)
{
var bytes = System.IO.File.ReadAllBytes(fileName);
System.IO.File.Delete(fileName);
return File(bytes, contentType, fileDownloadName);
}
}
顺便说一句,如果您正在处理非常大的文件,并且您担心内存消耗,则可以避免使用此方法。
【讨论】:
FileOptions.DeleteOnClose打开。这样,在您传递流并且 ASP 处理完流之后,流将被关闭并自动删除文件。 (系统崩溃除外)
创建文件并保存。
Response.Flush() 将所有数据发送给客户端。
然后你就可以删除临时文件了。
这对我有用:
FileInfo newFile = new FileInfo(Server.MapPath(tmpFile));
//create file, and save it
//...
string attachment = string.Format("attachment; filename={0}", fileName);
Response.Clear();
Response.AddHeader("content-disposition", attachment);
Response.ContentType = fileType;
Response.WriteFile(newFile.FullName);
Response.Flush();
newFile.Delete();
Response.End();
【讨论】:
以上答案对我有帮助,这就是我最终得到的结果:
public class DeleteFileAttribute : ActionFilterAttribute
{
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
filterContext.HttpContext.Response.Flush();
var filePathResult = filterContext.Result as FilePathResult;
if (filePathResult != null)
{
System.IO.File.Delete(filePathResult.FileName);
}
}
}
【讨论】:
您可以使用 OnActionExecuted 方法为操作创建自定义操作过滤器,然后在操作完成后删除文件,类似于
public class DeleteFileAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
// Delete file
}
}
那么你的动作有
[DeleteFileAttribute]
public FileContentResult GetFile(int id)
{
...
}
【讨论】:
我在 WebAPI 中执行了相同的操作。我需要在下载表单服务器后删除文件。 我们可以创建自定义响应消息类。以文件路径为参数,传输后删除。
public class FileHttpResponseMessage : HttpResponseMessage
{
private readonly string filePath;
public FileHttpResponseMessage(string filePath)
{
this.filePath = filePath;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
File.Delete(filePath);
}
}
在下面的代码中使用这个类,一旦它被写入响应流,它将删除你的文件。
var response = new FileHttpResponseMessage(filePath);
response.StatusCode = HttpStatusCode.OK;
response.Content = new StreamContent(new FileStream(filePath, FileMode.Open, FileAccess.Read));
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "MyReport.pdf"
};
return response;
【讨论】:
Content.Dispose() - 它是在base.Dispose(diposing)调用中调用的。
重写 OnResultExecuted 方法可能是正确的解决方案。该方法在响应写入后运行。
public class DeleteFileAttribute : ActionFilterAttribute
{
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
filterContext.HttpContext.Response.Flush();
// Delete file
}
}
动作代码:
[DeleteFileAttribute]
public FileContentResult GetFile(int id)
{
//your action code
}
【讨论】:
这里是基于@biesiad 为 ASP.NET MVC (https://stackoverflow.com/a/4488411/1726296) 提供的优雅解决方案的更新答案
基本上它在响应发送后返回 EmptyResult。
public ActionResult GetFile()
{
string theFilename = "<Full path your file name>"; //Your actual file name
Response.Clear();
Response.AddHeader("content-disposition", "attachment; filename=<file name to be shown as download>"); //optional if you want forced download
Response.ContentType = "application/octet-stream"; //Appropriate content type based of file type
Response.WriteFile(theFilename); //Write file to response
Response.Flush(); //Flush contents
Response.End(); //Complete the response
System.IO.File.Delete(theFilename); //Delete your local file
return new EmptyResult(); //return empty action result
}
【讨论】:
试试这个。这将正常工作。
public class DeleteFileAttribute : ActionFilterAttribute
{
public override void OnResultExecuted( ResultExecutedContext filterContext )
{
filterContext.HttpContext.Response.Flush();
string filePath = ( filterContext.Result as FilePathResult ).FileName;
File.Delete( filePath );
}
}
【讨论】:
我用过的图案。
1)创建文件。
2)删除旧创建的文件,FileInfo.CreationTime
3)用户下载。
这个想法怎么样?
【讨论】:
解决方案:
应该继承 FileResult 或创建自定义操作过滤器,但棘手的部分是在尝试删除文件之前刷新响应。
【讨论】:
我更喜欢返回HttpResponseMessage 的解决方案。我喜欢 Risord 的简单回答,所以我以同样的方式创建了一个流。然后,我没有返回File,而是将HttpResponseMessage.Content 属性设置为StreamContent 对象。
var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.None, 4096, FileOptions.DeleteOnClose);
return new HttpResponseMessage()
{
Content = new StreamContent(fs)
};
【讨论】:
我已经在https://stackoverflow.com/a/43561635/1726296发布了这个解决方案
public ActionResult GetFile()
{
string theFilename = "<Full path your file name>"; //Your actual file name
Response.Clear();
Response.AddHeader("content-disposition", "attachment; filename=<file name to be shown as download>"); //optional if you want forced download
Response.ContentType = "application/octet-stream"; //Appropriate content type based of file type
Response.WriteFile(theFilename); //Write file to response
Response.Flush(); //Flush contents
Response.End(); //Complete the response
System.IO.File.Delete(theFilename); //Delete your local file
return new EmptyResult(); //return empty action result
}
【讨论】: