2010-02-22 13 views
17

En el pasado, siempre he utilizado un objeto FileStream para escribir o reescribir un archivo completo después del cual cerraría inmediatamente la transmisión. Sin embargo, ahora estoy trabajando en un programa en el que quiero mantener un FileStream abierto para permitir que el usuario conserve el acceso al archivo mientras están trabajando entre guarda y salva. (Vea mi previous question).Reutilización de una ruta de archivos

Estoy usando XmlSerializer para serializar mis clases a un archivo desde y XML. Pero ahora mantengo el FileStream abierto para ser utilizado para guardar (reserializado) la instancia de mi clase más tarde. ¿Hay alguna consideración especial que deba hacer si reutilizo el mismo File Stream una y otra vez, en comparación con el uso de un nuevo flujo de archivos? ¿Debo reiniciar la transmisión al comienzo entre las grabaciones? Si un guardado posterior es de menor tamaño que el guardado anterior, FileStream dejará los bytes restantes del archivo anterior y, por lo tanto, creará un archivo dañado. ¿Debo hacer algo para borrar el archivo, así se comportará como si estuviera escribiendo un archivo completamente nuevo cada vez?

Respuesta

14

Su sospecha es correcta: si restablece la posición de una secuencia de archivos abierta y escribe contenido más pequeño que el que ya está en el archivo, dejará los datos finales y dará como resultado un archivo corrupto (según su definición de "corrupto"). ", por supuesto).

Si desea sobrescribir el archivo, debe cerrar la secuencia cuando haya terminado y crear una nueva secuencia cuando esté listo para volver a guardar.

Noté por su pregunta vinculada que mantiene el archivo abierto para evitar que otros usuarios le escriban al mismo tiempo. Probablemente esta no sea mi elección, pero si va a hacer eso, entonces creo que puede "borrar" el archivo invocando stream.SetLength(0) entre sucesivos guardados.

+0

¿Cuál sería su elección entonces? No quiero permitir que dos usuarios abran el archivo al mismo tiempo pensando que ambos tienen acceso de escritura y cada uno sobreescribe los guardados del otro. –

+0

Una de las respuestas más comunes es crear un "archivo de bloqueo" de longitud cero. Esto se puede combinar con mantener un 'FileStream' abierto indefinidamente, por supuesto, pero muchas aplicaciones optan solo por el archivo de bloqueo, ya que puede ser anulado si es necesario. Supongo que depende de la naturaleza de tu aplicación y del entorno para compartir. Tal vez sea la mejor opción. – Aaronaught

+0

Set-length (0) parece hacer lo que intento hacer. –

0

Según su pregunta, creo que sería mejor que cierre/vuelva a abrir el archivo subyacente. No pareces estar haciendo otra cosa que escribir el archivo completo. El valor que puede agregar al volver a escribir Abrir/Cerrar/Enjuagar/Buscar estará al lado de 0. Concéntrese en su problema comercial.

+0

Sí, solo estoy reescribiendo todo el archivo. Entonces, ¿solo sugiere mantener la secuencia abierta para bloquear el acceso al archivo, y luego cerrar y volver a abrir rápidamente la transmisión cuando necesito volver a escribir el archivo? –

+0

¿Por qué quieres mantener el archivo abierto? ¿Para qué sirve el negocio? –

+0

No desea que otro proceso capture el archivo después de que se cierre y escribir sobre sobrescribiendo los cambios que está realizando otro proceso actualmente. – Despertar

7

Otra opción podría ser usar SetLength (0) para truncar el archivo antes de comenzar a reescribirlo.

10

Existen varias formas de hacerlo; Si usted vuelve a abrir el archivo, tal vez configurarlo para truncar:

using(var file = new FileStream(path, FileMode.Truncate)) { 
    // write 
} 

Si va a sobrescribir el archivo, mientras que ya está abierto, a continuación, sólo se recorta después de escribir:

file.SetLength(file.Position); // assumes we're at the new end 

me gustaría tratar de evite eliminar/recrear, ya que esto pierde cualquier ACL, etc.

1

Recientemente se presentó el mismo requisito. De hecho, anteriormente, solía crear un nuevo FileStream dentro de una declaración using y sobrescribir el archivo anterior. Parece la cosa simple y efectiva para hacer.

using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write) 
{ 
    ProtoBuf.Serializer.Serialize(stream , value); 
} 

Sin embargo, me encontré con problemas de bloqueo donde algún otro proceso está bloqueando el archivo de destino. En mi intento de frustrar esto, reintenté la escritura varias veces antes de subir el error a la pila.

int attempt = 0; 
while (true) 
{ 
    try 
    { 
     using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write) 
     { 
     ProtoBuf.Serializer.Serialize(stream , value); 
     } 
     break; 
    } 
    catch (IOException) 
    { 
     // could be locked by another process 
     // make up to X attempts to write the file 
     attempt++; 
     if (attempt >= X) 
     { 
     throw; 
     } 
     Thread.Sleep(100); 
    } 
} 

Eso parecía funcionar para casi todo el mundo. Luego apareció esa máquina problemática y me obligó a seguir el camino de mantener un bloqueo en el archivo todo el tiempo.Entonces, en lugar de volver a intentar escribir el archivo en el caso de que ya esté bloqueado, ahora me estoy asegurando de mantener y mantener abierta la ruta para que no haya problemas de bloqueo con las escrituras posteriores.

int attempt = 0; 
while (true) 
{ 
    try 
    { 
     _stream = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.Read); 
     break; 
    } 
    catch (IOException) 
    { 
     // could be locked by another process 
     // make up to X attempts to open the file 
     attempt++; 
     if (attempt >= X) 
     { 
     throw; 
     } 
     Thread.Sleep(100); 
    } 
} 

Ahora, cuando escribo el archivo de la posición FileStream se debe restablecer a cero, como se dijo Aaronaught. Opté por "borrar" el archivo llamando al _stream.SetLength(0). Parecía la elección más simple. Luego, utilizando nuestro serializador de elección, protobuf-net de Marc Gravell, serialice el valor de la transmisión.

_stream.SetLength(0); 
ProtoBuf.Serializer.Serialize(_stream, value); 

Esto funciona bien la mayor parte del tiempo y el archivo está completamente escrito en el disco. Sin embargo, en algunas ocasiones he observado que el archivo no se escribe inmediatamente en el disco. Para garantizar que la transmisión se vacíe y el archivo esté completamente escrito en el disco, también tuve que llamar al _stream.Flush(true).

_stream.SetLength(0); 
ProtoBuf.Serializer.Serialize(_stream, value); 
_stream.Flush(true); 
Cuestiones relacionadas