8

在将一些处理后的内容写入输出流之后,我需要重新访问流的开头并写出一些内容元数据。我正在写入的数据非常大,高达 4Gb,并且可以直接写入文件或内存缓冲区,具体取决于各种环境因素。

如何实现一个允许我在完成内容写入后写出标题的 OutputStream?

4

4 回答 4

10

这是一个随机访问文件输出流。

请注意,如果将其用于大量流式输出,您可以将其临时包装在 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
于 2009-05-05T17:15:40.823 回答
2

如果您知道标头的大小,您可以先写一个空白标头,然后在最后返回修复它RandomAccessFile。如果您不知道标头的大小,那么您有一个基本原则,即文件系统通常不允许您插入数据。所以你需要先写入一个临时文件,然后再写入真正的文件。

于 2009-05-05T16:29:41.400 回答
1

Lucene 似乎有一个实现;并且 api 看起来不错。

getFilePointer()
void seek(long pos)  

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

我猜他们包装了一个RandomAccessFile

于 2009-05-05T16:31:54.423 回答
0

一种方法是首先将初始内容写入内存缓冲区,然后将标头写入“真实”输出流,然后刷新缓冲内容,然后再写入非缓冲流。听起来初始段不会那么长,以使缓冲合理。至于实现它,您可以使用 ByteArrayOutputStream 进行缓冲,然后让您的 OutputStream 类将“真实”输出流作为参数;并根据需要在两者之间切换。您可能需要扩展 OutputStream API 以允许定义要写入的元数据,因为这会触发从缓冲模式切换。

正如另一个答案所提到的,RandomAccessFile 也可以工作,尽管不会实现 OutputStream。

于 2009-05-05T16:32:21.177 回答