巻き戻すことができるOutputStreamを実装するにはどうすればよいですか?
-
05-07-2019 - |
質問
処理済みのコンテンツを出力ストリームに書き出した後、ストリームの先頭に戻り、コンテンツメタデータを書き出す必要があります。私が書いているデータは4Gbと非常に大きく、さまざまな環境要因に応じて、ファイルまたはメモリ内バッファに直接書き込まれます。
コンテンツの書き込みが完了した後にヘッダーを書き出すことができるOutputStreamを実装するにはどうすればよいですか?
解決
ランダムアクセスファイルの出力ストリームです。
大量のストリーム出力に使用する場合は、一時的にBufferedOutputStreamでラップして、大量の小さな書き込みを回避できることに注意してください(ラッパーを破棄するか、基礎となるストリームを直接使用する前にフラッシュするようにしてください)
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
他のヒント
ヘッダーのサイズがわかっている場合は、最初に空白のヘッダーを作成し、最後に戻って RandomAccessFile
で修正します。ヘッダーのサイズがわからない場合は、ファイルシステムでは一般にデータを挿入できないという基本事項があります。したがって、一時ファイルに書き込んでから、実際のファイルを書き込む必要があります。
Luceneには実装があるようです。 APIの外観は問題ありません。
getFilePointer()
void seek(long pos)
http://lucene.apache .org / java / 1_4_3 / api / org / apache / lucene / store / OutputStream.html
1つの方法は、最初にメモリバッファに最初のコンテンツを書き込み、次にヘッダーを「実際の」出力ストリームに書き込み、続いてバッファリングされたコンテンツをフラッシュし、そこから非バッファリングストリームに書き込むだけです。バッファリングを合理的にするために、最初のセグメントはそれほど長くはないようです。 実装に関しては、ByteArrayOutputStreamをバッファリングに使用してから、OutputStreamクラスに&quot; real&quot;を取得させることができます。引数としての出力ストリーム。必要に応じて2つを切り替えるだけです。 OutputStream APIを拡張して、書き込むメタデータを定義できるようにする必要があります。これにより、トリガーがバッファーモードから切り替わるのです。
他の回答で述べたように、RandomAccessFileも機能しますが、OutputStreamは実装しません。