Pergunta

Depois de escrever algum conteúdo processado em um fluxo de saída, preciso revisitar o início do fluxo e escrever alguns metadados de conteúdo. Os dados que estou escrevendo são muito grandes, até 4 GB, e podem ser escritos diretamente para um arquivo ou para um buffer na memória, dependendo de vários fatores ambientais.

Como posso implementar um outputStream que me permita escrever cabeçalhos depois de concluir a redação do conteúdo?

Foi útil?

Solução

Aqui está um fluxo de saída de arquivo de acesso aleatório.

Observe que, se usá -lo para uma grande quantidade de saída transmitida, você pode embrulhá -lo temporariamente em um fluxo tampão para evitar muitas gravações pequenas (apenas com certeza para descartá -lo antes de descartar o invólucro ou usar o fluxo subjacente diretamente).

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

Outras dicas

Se você souber o tamanho do cabeçalho, pode escrever um cabeçalho em branco inicialmente, depois volte para consertá -lo com RandomAccessFile no final. Se você não conhece o tamanho do cabeçalho, terá um fundamental que os sistemas de arquivos geralmente não permitem que você insira dados. Então você precisa gravar em um arquivo temporário e, em seguida, gravar o arquivo real.

Lucene parece ter uma implementação; E a API parece bem.

getFilePointer()
void seek(long pos)  

http://lucene.apache.org/java/1_4_3/api/org/apache/lucene/store/outputstream.html

Eu acho que eles envolvem um RandomAccessFile

Uma maneira seria escrever o conteúdo inicial em um buffer de memória primeiro, depois os cabeçalhos para o fluxo de saída 'real', seguido de descarga de conteúdo em buffer e, a partir daí, apenas escreva no fluxo não buffer. Parece que o segmento inicial não seria tanto tempo, para tornar o buffer razoável. Quanto a implementá -lo, você pode usar o BytearTrayOutputStream para buffer e, em seguida, fazer com que sua classe de saída de saída faça um fluxo de saída "real" como argumento; e basta alternar entre os dois conforme necessário. Pode ser necessário ampliar a API do OutputStream para permitir a definição de que metadados são escrever, pois isso gole -se mudando do modo buffer.

Conforme mencionado pela outra resposta, o RandomAccessFile também funcionaria, embora não implementasse o OutputStream.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top