返回FileStream后无法删除临时文件

我在C#MVC应用程序中有一个函数,它创建一个临时目录和一个临时文件,然后使用FileStream打开文件,将FileStream返回给调用函数,然后需要删除临时文件。 但是,我不知道如何删除临时目录和文件,因为它总是错误地说“进程无法访问该文件,因为它正被另一个进程使用”。 这是我尝试过的,但FileStream仍然使用finally块中的临时文件。 如何返回FileStream并删除临时文件?

public FileStream DownloadProjectsZipFileStream() { Directory.CreateDirectory(_tempDirectory); // temporary file is created here _zipFile.Save(_tempDirectory + _tempFileName); try { FileStream stream = new FileStream(_tempDirectory + _tempFileName, FileMode.Open); return stream; } finally { File.Delete(_tempDirectory + _tempFileName); Directory.Delete(_tempDirectory); } } 

FileStream返回的函数如下所示:

 public ActionResult DownloadProjects () { ProjectDownloader projectDownloader = new ProjectDownloader(); FileStream stream = projectDownloader.DownloadProjectsZipFileStream(); return File(stream, "application/zip", "Projects.zip"); } 

更新:我忘了提到zip文件是380 MB。 使用MemoryStream时,我遇到了系统内存不足的问题。

您可以创建一个包装类来实现Stream合约,并在内部包含FileStream ,以及维护文件的路径。

所有标准的Stream方法和属性都将传递给FileStream实例。

当这个包装类是Dispose d时,你会(在Dispose包装的FileStream )然后删除该文件。

这是我使用的上面的缩减版本:

 public class DeleteAfterReadingStream : FileStream { public DeleteAfterReadingStream(string path) : base(path, FileMode.Open) { } protected override void Dispose(bool disposing) { base.Dispose(disposing); if (File.Exists(Name)) File.Delete(Name); } } 

我使用Damien_The_Unbeliever(接受的答案)的建议,写下来,它的工作非常精彩。 我以为我会分享这堂课:

 public class BurnAfterReadingFileStream : Stream { private FileStream fs; public BurnAfterReadingFileStream(string path) { fs = System.IO.File.OpenRead(path); } public override bool CanRead { get { return fs.CanRead; } } public override bool CanSeek { get { return fs.CanRead; } } public override bool CanWrite { get { return fs.CanRead; } } public override void Flush() { fs.Flush(); } public override long Length { get { return fs.Length; } } public override long Position { get { return fs.Position; } set { fs.Position = value; } } public override int Read(byte[] buffer, int offset, int count) { return fs.Read(buffer, offset, count); } public override long Seek(long offset, SeekOrigin origin) { return fs.Seek(offset, origin); } public override void SetLength(long value) { fs.SetLength(value); } public override void Write(byte[] buffer, int offset, int count) { fs.Write(buffer, offset, count); } protected override void Dispose(bool disposing) { base.Dispose(disposing); if (Position > 0) //web service quickly disposes the object (with the position at 0), but it must get rebuilt and re-disposed when the client reads it (when the position is not zero) { fs.Close(); if (System.IO.File.Exists(fs.Name)) try { System.IO.File.Delete(fs.Name); } finally { } } } } 

问题是您只能在将文件写入响应后删除该文件,并且该文件仅在从操作返回后由FileStreamResult写入。

一种处理方法是创建将删除文件的FileResult子类。

FilePathResult子类化更容易,以便该类可以访问文件名。

 public class FilePathWithDeleteResult : FilePathResult { public FilePathResult(string fileName, string contentType) : base(string fileName, string contentType) { } protected override void WriteFile(HttpResponseBase response) { base.WriteFile(response); File.Delete(FileName); Directory.Delete(FileName); } } 

注意:我没有测试过上面的内容。 在使用它之前删除它的所有错误。

现在将控制器代码更改为:

 public ActionResult DownloadProjects () { Directory.CreateDirectory(_tempDirectory); // temporary file is created here _zipFile.Save(_tempDirectory + _tempFileName); return new FilePathWithDeleteResult(_tempDirectory + _tempFileName, "application/zip") { FileDownloadName = "Projects.zip" }; } 

我使用@hwiechers建议的方法,但使其工作的唯一方法是在删除文件之前关闭响应流。

这是源代码,请注意我在删除之前刷新流。

 public class FilePathAutoDeleteResult : FilePathResult { public FilePathAutoDeleteResult(string fileName, string contentType) : base(fileName, contentType) { } protected override void WriteFile(HttpResponseBase response) { base.WriteFile(response); response.Flush(); File.Delete(FileName); } } 

以下是Controller应该如何调用它:

 public ActionResult DownloadFile() { var tempFile = Path.GetTempFileName(); //do your file processing here... //For example: generate a pdf file return new FilePathAutoDeleteResult(tempFile, "application/pdf") { FileDownloadName = "Awesome pdf file.pdf" }; }