假设我想定义一个使用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 :(得分: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选项将确保操作系统在您关闭文件时自动删除临时文件。不需要特殊的Dispose方法,因为它都是为你完成的。
答案 1 :(得分:5)
这是一个有趣的想法,但是这个设计让我感到困扰。如果您已经在设计中解决了这个问题,请原谅我。但是如果你的设计只是FileStream
的一个简单包装,那么我认为这是一个微妙的问题。
如果您在关闭流时删除文件,则意味着实际使用文件中数据的唯一方法是FileAccess
是{{1} }。正确?换句话说,您将使用包含如下代码的文件:
ReadWrite
我看到的问题是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
的方法,则第二种方法将抛出异常。 (或者设置一个属性,其set访问器引发一个事件,其事件处理程序调度对写入TempStream
的方法的调用,这个问题可能最终会发生。)第一个将产生意外的结果为no明显的原因。
我认为,您可以让TempStream
班级使用TempFileStream
打开基础FileStream
来解决这个问题。然后实现关闭此FileAccess.Write
的{{1}}方法,并创建一个使用Rewind
的新方法。如果你这样做,任何试图在文件打开以进行读访问时写入的方法(反之亦然)将至少抛出异常。
答案 2 :(得分:3)
我知道这是一个较旧的主题,但这是另一种解决方案。我开始实现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 );
}
}
}
}
}
答案 3 :(得分: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添加适当的异常处理。
答案 4 :(得分:0)
基本上,根据TempFileStream逻辑,您总是使用具有唯一名称的创建文件(即Path.GetTempFileName所做的),并且您在使用后始终将其删除。因此,不需要提供接受FileMode的构造函数,因为您始终以相同的模式使用它。