2013-04-10 30 views
7

我在C#MVC應用程序中創建臨時目錄和臨時文件,然後使用FileStream打開文件,將FileStream返回給調用函數,然後需要刪除臨時文件。但是,我不知道如何刪除臨時目錄和文件,因爲它總是錯誤地指出「該進程無法訪問該文件,因爲它正在被另一個進程使用」。這是我的嘗試,但FileStream仍然使用finally塊中的臨時文件。我如何返回FileStream並刪除臨時文件?返回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

的'FileStream'打開了該文件,爲什麼你甚至希望能夠刪除的文件?這將使返回的FileStream不可用。 – 2013-04-10 18:52:51

+0

什麼是_zipFile的類型? – 2013-04-10 18:54:57

+2

你真的不需要該文件,而是保存到內存流中。 – 2013-04-10 18:57:31

回答

4

你可以創建一個實現Stream合同包裝類和包含內部的FileStream,以及維護文件的路徑。

所有標準的Stream方法和屬性都會傳遞給FileStream實例。

當這個包裝類是Dispose d,你會(在Dispose包裝FileStream)然後刪除該文件。

+0

這就是我最終做的。我只是擴展了FileStream並在構造函數中傳遞了臨時文件/目錄路徑,而在Dispose方法中,我添加了臨時文件和目錄的刪除代碼。感謝您的建議! – Bumper 2013-04-11 15:31:51

+1

@Bumber,你可以發佈你的代碼? – RayLoveless 2016-07-28 17:55:28

2

問題是,只有在寫入響應之後才能刪除該文件,並且只有在從操作返回後才由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" }; 
} 
3

我用了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 { } 
     } 
    } 
} 
4

下面是上述的縮減版本,我使用:

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); 
    } 
} 
0

我用@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" 
    }; 
}