2009-05-05 7 views
7

Po zapisaniu przetworzonej treści do strumienia wyjściowego, muszę ponownie zapoznać się z początkiem strumienia i napisać niektóre metadane treści. Dane, które piszę, są bardzo duże, nawet 4 GB i mogą być zapisane bezpośrednio do pliku lub do bufora w pamięci, w zależności od różnych czynników środowiskowych.Jak mogę zaimplementować OutputStream, który mogę przewinąć?

Jak mogę wdrożyć OutputStream, który pozwala mi wypisać nagłówki po zakończeniu pisania treści?

+0

Niestety, niestety, ale w dużej mierze dlatego, że musiałem zmienić swój projekt z powodu innych względów. To wciąż dobra odpowiedź. –

Odpowiedz

10

Oto losowy strumień wyjściowy pliku dostępu.

Należy zauważyć, że jeśli używa się go dla dużej ilości przesyłanych strumieniowo danych wyjściowych, można tymczasowo zawinąć je w zbuforowanym wyjściu, aby uniknąć wielu małych zapisów (należy się upewnić, że zostanie on wyczyszczony przed odrzuceniem opakowania lub bezpośrednim użyciem strumienia bazowego).

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

Jeśli znasz rozmiar nagłówka, możesz najpierw napisać pusty nagłówek, a następnie wrócić do niego, kończąc go na RandomAccessFile. Jeśli nie znasz rozmiaru nagłówka, masz fundamentalną zasadę, że systemy plików zazwyczaj nie pozwalają na wstawianie danych. Musisz więc napisać do pliku tymczasowego, a następnie napisać prawdziwy plik.

+0

Znamy rozmiar nagłówka, ale nie ma możliwości znalezienia ogólnego strumienia OuptutStream, który umożliwia zapisywanie w dowolnym punkcie strumienia. –

+0

Zapisujesz to. Zamknij plik. Otwórz plik RandomAccessFile. Napisz nagłówek. Zamknij plik RandomAccessFile. –

+1

(Zwróć uwagę, że RandomAccessFile na wydajność operacji jest do bani, więc używaj dużych operacji blokowych.) –

0

Jednym ze sposobów, byłoby napisać początkowej zawartości do bufora pamięci na początku, a następnie nagłówki język „prawdziwego” strumienia wyjściowego, a następnie spłukiwania buforowanej zawartość i od tego momentu po prostu zapisz w zbuforowanym strumieniu. Wygląda na to, że początkowy segment nie byłby aż tak długi, aby buforowanie było rozsądne. Jeśli chodzi o implementację, można użyć ByteArrayOutputStream do buforowania, a następnie przyjąć, że klasa OutputStream przyjmuje jako argument "prawdziwy" strumień wyjściowy; i w razie potrzeby przełącz się między nimi. Być może trzeba rozszerzyć interfejs API OutputStream, aby umożliwić zdefiniowanie metadanych do zapisu, ponieważ spowoduje to przełączenie z trybu buforowania.

Jak wspomniano w drugiej odpowiedzi, RandomAccessFile również by działało, chociaż nie zaimplementował OutputStream.

+1

"Dane, które piszę są bardzo duże, aż 4Gb" – DJClayworth