Как я могу прочитать определенное количество байтов из объекта FileInputStream, используя буферы
У меня есть ряд объектов, хранящихся в файле, как показано ниже:
sizeOfFile1 || file1 || sizeOfFile2 || file2 ...
Размер файлов - это сериализованные длинные объекты, а файлы - это просто необработанные байты файлов.
Я пытаюсь извлечь файлы из входного файла. Ниже мой код:
FileInputStream fileInputStream = new FileInputStream("C:\Test.tst");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
while (fileInputStream.available() > 0)
{
long size = (long) objectInputStream.readObject();
FileOutputStream fileOutputStream = new FileOutputStream("C:\" + size + ".tst");
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
int chunkSize = 256;
final byte[] temp = new byte[chunkSize];
int finalChunkSize = (int) (size % chunkSize);
final byte[] finalTemp = new byte[finalChunkSize];
while(fileInputStream.available() > 0 && size > 0)
{
if (fileInputStream.available() > finalChunkSize)
{
int i = fileInputStream.read(temp);
secBufferedOutputStream.write(temp, 0, i);
size = size - i;
}
else
{
int i = fileInputStream.read(finalTemp);
secBufferedOutputStream.write(finalTemp, 0, i);
size = 0;
}
}
bufferedOutputStream.close();
}
fileOutputStream.close();
Мой код не выполняется после того, как он прочитал первый sizeOfFile; он просто читает остальную часть входного файла в один файл, когда хранится несколько файлов.
Кто-нибудь может увидеть проблему здесь?
С уважением.
4 ответа
Оберните это в DataInputStream
и использовать readFully(byte[])
,
Но я ставлю под сомнение дизайн. Сериализация и произвольный доступ не смешиваются. Похоже, вы должны использовать базу данных.
NB вы злоупотребляете available()
, Смотрите страницу метода Javadoc. Никогда не правильно использовать его как счетчик общего количества байтов в потоке. Есть несколько правильных способов использования available()
и это не один из них.
Вы могли бы попробовать NIO вместо...
FileChannel roChannel = new RandomAccessFile(file, "r").getChannel();
ByteBuffer roBuf = roChannel.map(FileChannel.MapMode.READ_ONLY, 0, SIZE);
Это читает только байты размера из файла.
В
Это использует DataInput для чтения длинных. В этом конкретном случае я не использую readFully(), поскольку сегмент может быть слишком длинным, чтобы держать его в памяти:
DataInputStream in = new DataInputStream(FileInputStream());
byte[] buf = new byte[64*1024];
while(true) {
OutputStream out = ...;
long size;
try { size = in.readLong(); } catch (EOFException e) { break; }
while(size > 0) {
int len = (size > buf.length)?buf.length:size;
len = in.read(buf, 0, len);
out.write(buf, 0, len);
size-=len;
}
out.close();
}
Избавьте себя от многих проблем, выполнив одно из следующих действий:
- Переключитесь на использование Avro, поверьте мне, вы бы с ума сошли. Это легко учиться, и будет приспосабливать изменения схемы. Использование ObjectXXXStream - одна из худших идей, когда вы изменяете свою схему, ваши старые файлы становятся мусором.
- или используйте Thrift
- или используйте Hibernate (но это, вероятно, не лучший вариант, hibernate занимает много времени для изучения и требует много настроек)
Если вы действительно отказываетесь переключаться на avro, я рекомендую прочитать в классе Apache IOUtils. У него есть метод для копирования из одного входного потока в другой, что избавляет вас от головной боли. К сожалению, то, что вы хотите сделать, немного сложнее, вы хотите, чтобы размер каждого файла начинался с префикса. Вы можете использовать комбинацию объектов SequenceInputStream для этого.
Есть также GzipOutputStream и ZipOutputStream, но я думаю, что для них также нужны некоторые другие jar-файлы, добавленные в ваш classpath.
Я не собираюсь писать пример, потому что я честно думаю, что вы должны просто изучить avro или thrift и использовать это.