Чтение и запись ByteBuffer в файл: неужели все настолько неудобно?
Я работаю с файлами произвольного доступа фиксированного размера. Мне нужно читать из них, в произвольных позициях, блоки фиксированного размера в ByteBuffer и записывать их обратно, тоже в произвольных позициях. Я пишу и читаю блоки, заведомо попадающие внутрь файла - примерно как в БД.
Естественно, применяется RandomAccessFile и его FileChannel.
Я использую две техники (переключаемые).
Первая простейшая - мапирование: FileChannel.map. Удобно, эффективно и пр. Однако, увы, в 1.6 авторы до сих пор не исправили ошибку (исправленную в early-release 1.7): при более чем одном мапировании сборщик мусора может глюкануть, и приложение "вылетит". Да и другие проблемы есть: скажем, файл нельзя удалить, пока последний MappedByteBuffer не будет утилизован сборщиком мусора.
Вторая техника - FileChannel.read/write. Вначале я особо не задумывался: просто вызывал read(ByteBuffer dst, long position) и write(ByteBuffer src, long position). Ведь у меня - дисковый файл плюс уверенность, что блок не выходит за пределы файла. И только сейчас коллега обратил мое внимание, что эти методы возвращают результат, и нигде не написано, что они обязаны прочитать/записать полный заказанный объем. Разве что в тоне "вероятно, может быть". Например: http://java.sun.com/javase/6/docs/api/java/nio/channels/ReadableByteChannel.html#read(java.nio.ByteBuffer)
Что ж, я написал следующие процедуры:
(Комментарии пропускаю - JavaDoc нарушает форматирование LJ.)
Но неужели и правда я обязан писать таких "монстриков"? Ведь вроде самая что ни на есть стандартная задача. Может быть, есть готовое решение? Или где-то явно сказано, что FileChannel.read/write в случае RandomAccessFile вполне надежны?
Естественно, применяется RandomAccessFile и его FileChannel.
Я использую две техники (переключаемые).
Первая простейшая - мапирование: FileChannel.map. Удобно, эффективно и пр. Однако, увы, в 1.6 авторы до сих пор не исправили ошибку (исправленную в early-release 1.7): при более чем одном мапировании сборщик мусора может глюкануть, и приложение "вылетит". Да и другие проблемы есть: скажем, файл нельзя удалить, пока последний MappedByteBuffer не будет утилизован сборщиком мусора.
Вторая техника - FileChannel.read/write. Вначале я особо не задумывался: просто вызывал read(ByteBuffer dst, long position) и write(ByteBuffer src, long position). Ведь у меня - дисковый файл плюс уверенность, что блок не выходит за пределы файла. И только сейчас коллега обратил мое внимание, что эти методы возвращают результат, и нигде не написано, что они обязаны прочитать/записать полный заказанный объем. Разве что в тоне "вероятно, может быть". Например: http://java.sun.com/javase/6/docs/api/java/nio/channels/ReadableByteChannel.html#read(java.nio.ByteBuffer)
Что ж, я написал следующие процедуры:
public static void readAllBuffer(FileChannel fileChannel, long position, ByteBuffer dest)
throws IOException
{
if (position < 0)
throw new IllegalArgumentException("Negative position");
long savePosition = fileChannel.position();
try {
fileChannel.position(position);
ByteBuffer dup = dest.duplicate();
dup.rewind();
for (int ofs = 0, k = 0, n = dup.limit(); ofs < n; k++) {
if (k > 2 * n) { // too many attempts
throw new EOFException("Cannot read " + n + " bytes from the file at the position " + position);
}
int res = fileChannel.read(dup);
if (res < 0)
throw new EOFException("Cannot read " + n + " bytes from the file at the position " + position
+ ": file is exhausted");
ofs += res;
}
} finally {
fileChannel.position(savePosition);
}
}
public static void writeAllBuffer(FileChannel fileChannel, long position, ByteBuffer src)
throws IOException
{
if (position < 0)
throw new IllegalArgumentException("Negative position");
long savePosition = fileChannel.position();
try {
fileChannel.position(position);
ByteBuffer dup = src.duplicate();
dup.rewind();
for (int ofs = 0, k = 0, n = src.limit(); ofs < n; k++) {
if (k > 2 * n) { // too many attempts
throw new EOFException("Cannot write " + n + " bytes to the file at the position " + position);
}
int res = fileChannel.write(dup);
if (res < 0)
throw new EOFException("Cannot write " + n + " bytes to the file at the position " + position
+ ": the disk if probably full");
ofs += res;
}
} finally {
fileChannel.position(savePosition);
}
}
(Комментарии пропускаю - JavaDoc нарушает форматирование LJ.)
Но неужели и правда я обязан писать таких "монстриков"? Ведь вроде самая что ни на есть стандартная задача. Может быть, есть готовое решение? Или где-то явно сказано, что FileChannel.read/write в случае RandomAccessFile вполне надежны?
