7

假设我想定义一个使用 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)
 {
   /// ???
 }

}   

我应该如何简单安全地实现这一点?

4

5 回答 5

28

试试这个:

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 选项将确保操作系统在您关闭文件时自动删除临时文件。不需要特殊的 Dispose 方法,因为它已经为您处理好了。

于 2010-06-28T16:52:51.237 回答
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班级FileStream使用FileAccess.Write. 然后实现一个Rewind方法来关闭它FileStream并创建一个新的使用FileAccess.Read. 如果您这样做,任何在打开文件以进行读取访问(反之亦然)时尝试写入文件的方法至少会引发异常。

于 2009-10-05T18:21:58.893 回答
3

我知道这是一个较旧的线程,但这是一个替代解决方案。我开始实现 TempFileStream,但我想要更多的并发性。我的用例涉及通过 MVC 将 [可能 MB 的] 数据库结果导出到 CSV 文件。我想从数据库查询中获得数据后立即开始下载到客户端,而不是在开始下载之前等待写入可能很大的临时文件。

在此解决方案中,我在填充 AnonymousPipeStream 的单独线程中启动查询。然后,主线程可以从管道的另一端获取可用的数据。它使用 .Net 4 任务。

希望其他人觉得这很有用。

-抢

控制器方法:

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 );
                }
            }
        }
    }
}
于 2011-09-07T12:58:01.990 回答
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 添加适当的异常处理。

于 2009-10-05T11:16:36.553 回答
0

基本上根据 TempFileStream 逻辑,您始终使用刚刚创建的具有唯一名称的文件(这就是 Path.GetTempFileName 所做的),并且您始终在使用后将其删除。所以不需要提供接受 FileMode 的构造函数,因为你总是在相同的模式下使用它。

于 2009-10-05T11:22:23.007 回答