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

StackOverflow https://stackoverflow.com/questions/825732

  •  05-07-2019
  •  | 
  •  

Вопрос

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

Как я могу реализовать 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 / орг / Apache / Lucene / магазин / OutputStream.html

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

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

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

scroll top