8

我在 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 时出现系统内存不足异常。

4

5 回答 5

7

这是我使用的上述内容的缩减版本:

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);
    }
}
于 2014-05-26T06:04:13.373 回答
5

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

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

当此包装类为Disposed 时,您将(在Disposeing 包装后FileStream)然后删除该文件。

于 2013-04-11T06:14:55.473 回答
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 { }
        }
    }
}
于 2013-10-07T13:58:40.307 回答
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" };
}
于 2013-04-11T05:58:32.100 回答
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);
    }

}

这是控制器应该如何调用它:

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"
    };
}
于 2015-04-08T12:16:58.333 回答