我在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时出现系统内存不足的问题。
答案 0 :(得分:6)
这是我使用的上述缩减版本:
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);
}
}
答案 1 :(得分:4)
您可以创建一个包装类来实现Stream
合同,并在内部包含FileStream
,并维护文件的路径。
所有标准Stream
方法和属性都只会传递给FileStream
实例。
当此包装类为Dispose
d时,您(在Dispose
包裹的FileStream
之后)将删除该文件。
答案 2 :(得分:4)
我使用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 { }
}
}
}
答案 3 :(得分:1)
问题是您只能在文件写入响应后删除该文件,并且该文件仅在从操作返回后由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" };
}
答案 4 :(得分:1)
我使用@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"
};
}