2009-10-05 59 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); 
       } 
      } 
     } 
    } 
}