Java: чтение из InputStream не всегда читает один и тот же объем данных - PullRequest
5 голосов
/ 24 октября 2011

Хорошо это или плохо, я без проблем использую код, подобный следующему:

ZipFile aZipFile = new ZipFile(fileName);   
InputStream zipInput = aZipFile.getInputStream(name);  
int theSize = zipInput.available();  
byte[] content = new byte[theSize];  
zipInput.read(content, 0, theSize);

Я использовал его (эта логика получения доступного размера и чтения непосредственно в байтовый буфер) для File I / O без проблем, и я использовал его также с zip-файлами.

Но недавно я обнаружил, что zipInput.read(content, 0, theSize); на самом деле читает на 3 байта меньше, чем theSize.

И поскольку код не находится в цикле для проверки длины, возвращаемой zipInput.read(content, 0, theSize); Я прочитал файл с отсутствующими 3 последними байтами
и позже программа не может функционировать должным образом (файл является двоичным файлом).

Достаточно странно с разными zip-файлами большего размера, например 1075 байт (в моем случае проблемная запись zip составляет 867 байт), код работает отлично!

Я понимаю, что логика кода, вероятно, не самая "лучшая", но почему я вдруг получаю эту проблему сейчас?

А как получится, если я сразу запустлю программу с большей записью zip, она работает?

Любые пожелания приветствуются

Спасибо

Ответы [ 3 ]

7 голосов
/ 24 октября 2011

Из InputStream read Документов API:

Предпринята попытка прочитать столько же, сколько и байтов, но меньшее число можно прочитать.

... и:

Возвращает: общее количество байтов, считанных в буфер, или -1, если данных больше нет, потому что достигнут конец потока.

Другими словами, если только метод read не возвращает -1, все еще доступно больше данных для чтения, но вы не можете гарантировать, что read будет считывать точно указанное количество байтов. Указанное количество байтов - это верхняя граница , описывающая максимум объема данных, которые он будет читать.

2 голосов
/ 24 октября 2011

Использование available() не гарантирует, что он подсчитал общее количество доступных байтов для end of stream.
См. Метод Java InputStream * available() .В нем говорится, что

Возвращает оценку количества байтов, которые могут быть прочитаны (или пропущены) из этого входного потока без блокировки при следующем вызове метода для этого входного потока.Следующим вызовом может быть тот же поток или другой поток.Одно чтение или пропуск этого количества байтов не будет блокировать, но может прочитать или пропустить меньшее количество байтов.

Обратите внимание, что хотя некоторые реализации InputStream будут возвращать общее количество байтов в потоке, многие не будут возвращать.Никогда не правильно использовать возвращаемое значение этого метода для выделения буфера, предназначенного для хранения всех данных в этом потоке.

Пример решения вашей проблемы может быть следующим:

ZipFile aZipFile = new ZipFile(fileName);   
InputStream zipInput = aZipFile.getInputStream( caImport );  
int available = zipInput.available();  
byte[] contentBytes = new byte[ available ];  
while ( available != 0 )   
{   
    zipInput.read( contentBytes );   
    // here, do what ever you want  
    available = dis.available();  
} // while available  
...   

Это точно работает для всех размеров входных файлов.

0 голосов
/ 19 января 2017

Лучший способ сделать это должен быть следующим:

public static byte[] readZipFileToByteArray(ZipFile zipFile, ZipEntry entry)
    throws IOException {
    InputStream in = null;
    try {
        in = zipFile.getInputStream(entry);
        return IOUtils.toByteArray(in);
    } finally {
        IOUtils.closeQuietly(in);
    }
}

где метод IOUtils.toByteArray (in) продолжает чтение до EOF, а затем возвращает байтовый массив.

...