Как я могу реализовать OutputStream, который я могу перемотать?

После записи некоторого обработанного контента в выходной поток мне нужно вернуться к началу потока и записать некоторые метаданные контента. Данные, которые я записываю, очень велики, целых 4 ГБ, и могут быть записаны либо непосредственно в файл, либо в буфер в памяти, в зависимости от различных факторов окружающей среды.

Как я могу реализовать OutputStream, который позволяет мне записывать заголовки после завершения написания контента?


person Chris R    schedule 05.05.2009    source источник
comment
К сожалению, этого не произошло, но в основном потому, что мне пришлось изменить свой дизайн по другим соображениям. Это все еще хороший ответ.   -  person Chris R    schedule 13.08.2009


Ответы (4)


Вот поток вывода файла произвольного доступа.

Обратите внимание, что если вы используете его для большого количества потокового вывода, вы можете временно обернуть его в 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
person Lawrence Dol    schedule 05.05.2009

Если вы знаете размер заголовка, вы можете сначала написать пустой заголовок, а затем вернуться, чтобы исправить его с помощью RandomAccessFile в конце. Если вы не знаете размер заголовка, то у вас есть фундаментальное правило, что файловые системы обычно не позволяют вам вставлять данные. Поэтому вам нужно записать во временный файл, а затем записать реальный файл.

person Tom Hawtin - tackline    schedule 05.05.2009
comment
Нам известен размер заголовка, но мы никак не можем найти универсальный OuptutStream, который позволял бы писать в произвольную точку потока. - person Chris R; 05.05.2009
comment
Вы это выпишите. Закройте файл. Откройте RandomAccessFile. Напишите заголовок. Закройте RandomAccessFile. - person Tom Hawtin - tackline; 05.05.2009
comment
(Обратите внимание, что производительность RandomAccessFile на операцию отстой, поэтому используйте операции с большими блоками.) - person Tom Hawtin - tackline; 05.05.2009

У Lucene, кажется, есть реализация; и API выглядит нормально.

getFilePointer()
void seek(long pos)  

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

Я предполагаю, что они обертывают RandomAccessFile

person KarlP    schedule 05.05.2009

Одним из способов было бы сначала записать начальное содержимое в буфер памяти, затем заголовки в «реальный» поток вывода, а затем сбросить буферизованное содержимое, а оттуда просто записать в небуферизованный поток. Похоже, что начальный сегмент не будет таким длинным, чтобы сделать буферизацию разумной. Что касается его реализации, вы можете использовать ByteArrayOutputStream для буферизации, а затем ваш класс OutputStream принимает «настоящий» выходной поток в качестве аргумента; и просто переключайтесь между ними по мере необходимости. Возможно, вам потребуется расширить API OutputStream, чтобы разрешить определение метаданных для записи, поскольку это вызывает переключение из режима буферизации.

Как упоминалось в другом ответе, RandomAccessFile тоже будет работать, хотя и не будет реализовывать OutputStream.

person StaxMan    schedule 05.05.2009
comment
Данные, которые я пишу, очень большие, целых 4Gb - person DJClayworth; 05.05.2009