2009-10-05 20 views
6

Digamos que quiero definir una clase TempFileStream que crea un archivo temporal utilizando el método Path.GetTempFileName(). Se debe eliminar un archivo temporal cuando ya no se necesita el objeto de TempFileStream, p. cerrado o dispuesto:Manejo con la secuencia de archivos temporales

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

    /// ... 

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

} 

¿Cómo debo implementar esto de forma sencilla y segura?

+0

¿Tiene que usar FileStream para esto, no puede usted utilizar MemoryStream? De esta forma, no tiene que manejar todos los posibles problemas relacionados con la eliminación del archivo. – armannvg

+0

@armannvg, ¿de qué problemas estás hablando? Es un almacenamiento temporal para el archivo muy grande antes de que se grabe en la base de datos. – sh0gged

+0

Solo los problemas habituales de eliminación de archivos -> IOException, UnauthorizedAccessException etc. Pero si está trabajando con un archivo muy grande, entonces MemoryStream no es una opción – armannvg

Respuesta

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

Debe agregar el manejo de excepciones adecuado para File.Delete si es necesario.

0

Básicamente, según la lógica de TempFileStream, siempre utiliza el archivo recién creado con un nombre único (esto es lo que hace Path.GetTempFileName) y siempre lo elimina después de su uso. Por lo tanto, no es necesario proporcionar un constructor que acepte FileMode ya que siempre lo usa en el mismo modo.

+0

Buen punto. :) Gracias. – sh0gged

5

Esta es una idea interesante, pero hay algo en este diseño que me preocupa. Perdóname si ya has abordado esto en tu diseño. Pero si su diseño es simplemente una envoltura simple alrededor de FileStream, hay un problema sutil, pero creo que significativo.

Si va a eliminar el archivo cuando se cierra el flujo, lo que significa que la única manera de realmente uso de los datos en el archivo es si el FileAccess es ReadWrite. ¿Correcto? En otras palabras, usted va a utilizar el archivo con el código que se parece a esto:

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

El problema que veo es que ReadDataFromTempFile espera que el archivo que se abrirá para acceso de lectura, no acceso de lectura/escritura. Y esto abre la puerta a algunos errores que creo que serán muy difíciles de encontrar. Considere código como este:

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

... si se compara con esto:

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

Claro, el primer método es más corta que la segunda. Pero el segundo método lanzará una excepción si ProcessOutput llama a un método que escribe en TempStream. (O establece una propiedad cuyo acceso de conjunto provoca un evento cuyo controlador de eventos distribuye una llamada a un método que escribe en TempStream, que indica cómo este problema probablemente terminará sucediendo). El primero solo producirá resultados inesperados sin motivo aparente.

Puede evitar esto, creo, teniendo su clase TempFileStream abra el FileStream subyacente usando FileAccess.Write. A continuación, implemente un método Rewind que cierra este FileStream y crea uno nuevo que usa FileAccess.Read. Si lo hace, cualquier método que intente escribir en el archivo mientras está abierto para acceso de lectura (o viceversa) arrojará al menos una excepción.

+0

Buen punto, muchas gracias. Debería tener esto en cuenta. Inicialmente pensé en un simple envoltorio. – sh0gged

26

prueba este lugar:

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) { } 
} 

La opción FileOptions.DeleteOnClose se asegurará de que el sistema operativo borra el archivo temporal automáticamente al cerrar el archivo. No es necesario un método especial de eliminación, ya que todo se solucionó por usted.

+2

+1 para DeleteOnClose, no lo conocía, ¡es útil! –

+2

+1, me encanta cuánto aprendo solo leyendo las respuestas de otras personas en este sitio. – shambulator

2

Sé que este es un hilo más antiguo, pero aquí hay una solución alternativa. Empecé a implementar TempFileStream, pero quería más concurrencia.Mi caso de uso implica exportar [potencialmente MBs] de resultados de bases de datos a un archivo CSV a través de MVC. Quiero comenzar a descargar al cliente tan pronto como haya datos disponibles a partir de la consulta de la base de datos, en lugar de esperar a que se escriba un archivo temporal potencialmente grande antes de comenzar la descarga.

En esta solución, ejecuto la consulta en un hilo separado que llena un AnonymousPipeStream. El hilo principal puede sorber los datos del otro extremo de la tubería como está disponible. Utiliza .Net 4 tareas.

Espero que alguien más lo encuentre útil.

-Rob

método de control:

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

Modelo:

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); 
       } 
      } 
     } 
    } 
} 
Cuestiones relacionadas