Domanda

Dopo aver scritto del contenuto elaborato su un flusso di output, devo rivisitare l'inizio del flusso e scrivere alcuni metadati del contenuto. I dati che sto scrivendo sono molto grandi, fino a 4 GB, e possono essere scritti direttamente in un file o in un buffer in memoria, a seconda di vari fattori ambientali.

Come posso implementare un OutputStream che mi permetta di scrivere le intestazioni dopo aver completato la scrittura del contenuto?

È stato utile?

Soluzione

Ecco un flusso di output del file ad accesso casuale.

Nota che se lo usi per una grande quantità di output in streaming puoi temporaneamente avvolgerlo in un BufferedOutputStream per evitare molte piccole scritture (assicurati solo di scaricarlo prima di scartare il wrapper o usare direttamente il flusso sottostante).

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

Altri suggerimenti

Se conosci la dimensione dell'intestazione, puoi inizialmente scrivere un'intestazione vuota, quindi tornare indietro per risolverlo con RandomAccessFile alla fine. Se non si conosce la dimensione dell'intestazione, è fondamentale che i filesystem generalmente non consentano di inserire dati. Quindi è necessario scrivere su un file temporaneo e quindi scrivere il file reale.

Lucene sembra avere un'implementazione; e l'API sembra ok.

getFilePointer()
void seek(long pos)  

http: //lucene.apache .org / java / 1_4_3 / api / org / apache / Lucene / negozio / OutputStream.html

Credo che racchiudano un RandomAccessFile

Un modo sarebbe quello di scrivere prima i contenuti iniziali su un buffer di memoria, quindi le intestazioni in un flusso di output "reale", seguito da uno svuotamento dei contenuti bufferizzati e da lì semplicemente scrivere nel flusso non bufferizzato. Sembra che il segmento iniziale non sia così lungo, per rendere ragionevole il buffering. Per quanto riguarda l'implementazione, puoi usare ByteArrayOutputStream per il buffering e quindi far sì che la tua classe OutputStream prenda " real " flusso di output come argomento; e basta passare tra i due, se necessario. Potrebbe essere necessario estendere l'API OutputStream per consentire la definizione di cosa devono scrivere i metadati, in quanto i trigger passano dalla modalità buffer.

Come menzionato dall'altra risposta, anche RandomAccessFile funzionerebbe, sebbene non implementasse OutputStream.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top