2009-05-05 17 views
7

Después de escribir algo del contenido procesado en una secuencia de salida, necesito volver a visitar el comienzo de la transmisión y escribir algunos metadatos de contenido. Los datos que estoy escribiendo son muy grandes, tanto como 4Gb, y pueden escribirse directamente en un archivo o en un búfer en memoria, dependiendo de varios factores ambientales.¿Cómo puedo implementar un OutputStream que pueda rebobinar?

¿Cómo puedo implementar un OutputStream que me permite escribir encabezados después de completar la escritura del contenido?

+0

No lo hizo, lamentablemente, pero eso se debe en gran parte a que tuve que cambiar mi diseño debido a otras consideraciones. Sigue siendo una buena respuesta. –

Respuesta

10

Aquí hay una secuencia de salida de archivo de acceso aleatorio.

Tenga en cuenta que si lo utiliza para una gran cantidad de salida transmitida puede envolverlo temporalmente en un BufferedOutputStream para evitar muchas escrituras pequeñas (solo asegúrese de enjuagarlo antes de descartar el envoltorio o usar la transmisión subyacente directamente).

import java.io.*; 

/** 
* A positionable file output stream. 
* <p> 
* Threading Design : [x] Single Threaded [ ] Threadsafe [ ] Immutable [ ] Isolated 
*/ 

public class RandomFileOutputStream 
extends OutputStream 
{ 

// ***************************************************************************** 
// INSTANCE PROPERTIES 
// ***************************************************************************** 

protected RandomAccessFile    randomFile;        // the random file to write to 
protected boolean      sync;         // whether to synchronize every write 

// ***************************************************************************** 
// INSTANCE CONSTRUCTION/INITIALIZATON/FINALIZATION, OPEN/CLOSE 
// ***************************************************************************** 

public RandomFileOutputStream(String fnm) throws IOException { 
    this(fnm,false); 
    } 

public RandomFileOutputStream(String fnm, boolean syn) throws IOException { 
    this(new File(fnm),syn); 
    } 

public RandomFileOutputStream(File fil) throws IOException { 
    this(fil,false); 
    } 

public RandomFileOutputStream(File fil, boolean syn) throws IOException { 
    super(); 

    File        par;         // parent file 

    fil=fil.getAbsoluteFile(); 
    if((par=fil.getParentFile())!=null) { IoUtil.createDir(par); } 
    randomFile=new RandomAccessFile(fil,"rw"); 
    sync=syn; 
    } 

// ***************************************************************************** 
// INSTANCE METHODS - OUTPUT STREAM IMPLEMENTATION 
// ***************************************************************************** 

public void write(int val) throws IOException { 
    randomFile.write(val); 
    if(sync) { randomFile.getFD().sync(); } 
    } 

public void write(byte[] val) throws IOException { 
    randomFile.write(val); 
    if(sync) { randomFile.getFD().sync(); } 
    } 

public void write(byte[] val, int off, int len) throws IOException { 
    randomFile.write(val,off,len); 
    if(sync) { randomFile.getFD().sync(); } 
    } 

public void flush() throws IOException { 
    if(sync) { randomFile.getFD().sync(); } 
    } 

public void close() throws IOException { 
    randomFile.close(); 
    } 

// ***************************************************************************** 
// INSTANCE METHODS - RANDOM ACCESS EXTENSIONS 
// ***************************************************************************** 

public long getFilePointer() throws IOException { 
    return randomFile.getFilePointer(); 
    } 

public void setFilePointer(long pos) throws IOException { 
    randomFile.seek(pos); 
    } 

public long getFileSize() throws IOException { 
    return randomFile.length(); 
    } 

public void setFileSize(long len) throws IOException { 
    randomFile.setLength(len); 
    } 

public FileDescriptor getFD() throws IOException { 
    return randomFile.getFD(); 
    } 

} // END PUBLIC CLASS 
2

Si conoce el tamaño del encabezado, puede escribir un encabezado en blanco inicialmente y luego volver a arreglarlo con RandomAccessFile al final. Si no conoce el tamaño del encabezado, entonces tiene una fundamental que los sistemas de archivos generalmente no le permiten insertar datos. Entonces necesita escribir en un archivo temporal y luego escribir el archivo real.

+0

Sabemos el tamaño del encabezado, pero no hay forma de que podamos encontrar un OuptutStream genérico que permita escribir en un punto arbitrario de la secuencia. –

+0

Lo escribe. Cierra el archivo. Abra un RandomAccessFile. Escribe el encabezado. Cierre el RandomAccessFile. –

+1

(Tenga en cuenta que RandomAccessFile por rendimiento de operación es una mierda, así que use operaciones de bloques grandes). –

0

Uno sería escribir contenido inicial de una memoria intermedia primero, luego los encabezados en la corriente 'real' de salida, seguido por ras del buffer contenidos, y a partir de ahí simplemente escriba en la secuencia no protegida. Parece que el segmento inicial no sería tan largo, para hacer que el almacenamiento en búfer sea razonable. En cuanto a su implementación, puede usar ByteArrayOutputStream para el almacenamiento en búfer, y luego hacer que su clase OutputStream tome el flujo de salida "real" como argumento; y solo cambia entre los dos según sea necesario. Es posible que necesite extender la API de OutputStream para permitir la definición de los metadatos a escribir, ya que eso activa el cambio del modo de almacenamiento en búfer.

Como se menciona en la otra respuesta, RandomAccessFile también funcionaría, aunque no implementaría OutputStream.

+1

"Los datos que estoy escribiendo son muy grandes, hasta 4Gb" – DJClayworth

Cuestiones relacionadas