2009-10-05 96 views
6

處理說我要來定義,創建使用Path.GetTempFileName()方法的臨時文件的TempFileStream類。當不再需要TempFileStream對象時,必須刪除臨時文件,例如關閉或處理:臨時文件流

class TempFileStream: FileStream 
{ 
    string m_TempFileName = Path.GetTempFileName(); 
    public TempFileStream(FileMode fileMode): base(m_TempFileName,fileMode) {} 

    /// ... 

public ovverride Dispose(bool disposing) 
{ 
    /// ??? 
} 

} 

我應該如何實現這個簡單而安全?

+0

你必須使用的FileStream這一點,你不能使用的MemoryStream?這樣你就不必處理與刪除文件相關的所有可能的問題。 – armannvg 2009-10-05 12:01:15

+0

@armannvg,你在說什麼問題?這是臨時存儲非常大的文件,然後將其記錄到數據庫中。 – sh0gged 2009-10-05 12:09:11

+0

只是通常的文件刪除問題 - > IOException,UnauthorizedAccessException等,但如果你正在與一個非常大的文件,然後MemoryStream不是一個選項 – armannvg 2009-10-05 12:16:24

回答

2
base.Dispose(disposing); // disposes the base stream so the file is no longer used 
if (disposing) 
    File.Delete(m_TempFileName); // deletes the file 

如果需要,您應該爲File.Delete添加適當的異常處理。

0

基本上按照你總是使用剛剛創建的文件具有獨特的名稱(即Path.GetTempFileName做什麼),而你總是在使用之後將其刪除TempFileStream邏輯。因此,不需要提供接受FileMode的構造函數,因爲您始終在相同模式下使用它。

+0

好點。 :) 謝謝。 – sh0gged 2009-10-05 12:04:58

5

這是一個有趣的想法,但也有一些關於這個設計,困擾我。請原諒我,如果你已經在你的設計中解決了這個問題。但是如果你的設計只是一個簡單的包裝FileStream,那麼我認爲這是一個微妙的問題,但這是一個重大的問題。

如果你刪除文件時流被關閉,這意味着實際使用該文件中的數據是唯一的出路,如果FileAccessReadWrite。正確?換句話說,你將使用的代碼看起來像這樣的文件:

using (TempFileStream t as new TempFileStream()) 
{ 
    WriteDataToTempFile(t); 
    t.Seek(0, SeekOrigin.Begin); 
    ReadDataFromTempFile(t); 
} 

我看到的問題是,ReadDataFromTempFile期待文件進行讀取訪問被打開,而不是讀/寫訪問。這爲我認爲很難找到的一些錯誤打開了大門。考慮這樣的代碼:

using (TempFileStream t as new TempFileStream()) 
{ 
    MyClass o = new MyClass(o); 
    o.TempStream = t; 
    o.ProduceOutput(); 
    t.Seek(0, SeekOrigin.Begin); 
    o.ProcessOutput(); 
} 

...當這種比較:

MyClass o = new MyClass(); 
string n = Path.GetTempFileName(); 
using (FileStream s = new FileStream(n, FileMode.Create, FileAccess.Write)) 
{ 
    o.TempStream = t; 
    o.ProduceOutput(); 
} 
using (FileStream s = new FileStream(n, FileMode.Open, FileAccess.Read)) 
{ 
    o.TempStream = t; 
    o.ProcessOutput(); 
} 
File.Delete(n); 

當然,第一種方法比第二短。但如果ProcessOutput調用寫入TempStream的方法,則第二種方法將拋出異常。 (或設置其set訪問引發其事件處理程序分派到寫入TempStream的方法,它是如何這個問題最終可能會發生呼叫事件的性質。)第一個將只產生無緣無故意想不到的效果。

你可以通過讓你的TempFileStream類使用FileAccess.Write打開底層FileStream來解決這個問題。然後執行一個Rewind方法關閉此FileStream,並創建一個使用FileAccess.Read一個新的。如果你這樣做,任何試圖寫入文件的方法在讀取訪問時打開(反之亦然),至少會拋出一個異常。

+0

好點,非常感謝。我應該考慮到這一點。起初我只想過一個簡單的包裝。 – sh0gged 2009-10-06 08:05:54

26

試試這個來代替:

public class TempFileStream : FileStream 
{ 
    public TempFileStream() 
     : base(Path.GetTempFileName(), FileMode.Create, FileAccess.ReadWrite, FileShare.Read, 4096, FileOptions.DeleteOnClose) { } 
    public TempFileStream(FileAccess access) 
     : base(Path.GetTempFileName(), FileMode.Create, access, FileShare.Read, 4096, FileOptions.DeleteOnClose) { } 
    public TempFileStream(FileAccess access, FileShare share) 
     : base(Path.GetTempFileName(), FileMode.Create, access, share, 4096, FileOptions.DeleteOnClose) { } 
    public TempFileStream(FileAccess access, FileShare share, int bufferSize) 
     : base(Path.GetTempFileName(), FileMode.Create, access, share, bufferSize, FileOptions.DeleteOnClose) { } 
} 

的FileOptions.DeleteOnClose選項將確保OS會自動刪除臨時文件,當您關閉了該文件。無需使用特殊的Dispose方法,因爲這一切都爲您照顧。

+2

+1爲DeleteOnClose,不知道那個,來得方便! – 2010-06-28 16:56:04

+2

+1,我很喜歡在這個網站上閱讀其他人的答案。 – shambulator 2010-06-28 18:09:49

2

我知道這是一個較舊的線程,但這裏是一個替代的解決方案。我開始實現TempFileStream,但我想要更多的併發性。我的用例涉及通過MVC將[潛在MB]數據庫結果導出爲CSV文件。只要數據庫查詢中的數據可用,我就要開始下載到客戶端,而不是在開始下載之前等待寫入潛在的大型臨時文件。

在此解決方案中,我在一個單獨的線程中啓動查詢,該線程填充AnonymousPipeStream。主線程隨後可以從管道的另一端汲取數據。它使用.Net 4任務。

希望別人認爲這有用。

-Rob

控制器的方法:

public FileResult ResultExport (ReportOptions options) 
{ 
    ResultExport rpt = new ResultExport(options); 
    HttpContext.Response.BufferOutput = false; 
    return File(rpt.Export(), "text/csv", "results.csv"); 
} 

型號:

public ResultExport 
{ 
    private AnonymousPipeServerStream WriteStream = null; 

    public Stream Export() 
    { 
     // 
     // We'll fire off the database query in a background 
     // thread. It will write data to one end of the pipe. We'll return the reader 
     // end of that pipe to our caller. 
     // 
     WriteStream = new AnonymousPipeServerStream(PipeDirection.Out); 
     AnonymousPipeClientStream reader = new AnonymousPipeClientStream(PipeDirection.In, WriteStream.ClientSafePipeHandle); 

     // 
     // Call Execute() in a background thread. 
     // 
     Task.Factory.StartNew(() => Execute()); 

     // 
     // While Execute() is filling the pipe with data, 
     // return the reader end of the pipe to our caller. 
     // 
     return reader; 
    } 

    private void Execute() 
    { 
     // 
     // WriteStream should only by populated by Export() 
     // 
     if(WriteStream != null) 
     { 
      using (StreamWriter sw = new StreamWriter(WriteStream, Encoding.UTF8, 4096)) 
      { 
       // 
       // Shove data into the StreamWriter as we get it from the database 
       // 
       foreach (string line in ExportCore()) 
       { 
        // Each line is a comma-delimited set of values 
        sw.WriteLine(line); 
       } 
      } 
     } 
    } 
}