Avatar ·

Как преобразовать InputStream в массив byte[] в Java? Все способы

Всем привет! Я новичок в Java и столкнулся с такой проблемой: мне нужно прочитать все данные из InputStream (например, из файла или из HTTP-ответа) и получить их в виде массива byte[]. Но я не знаю, как это правильно сделать.

Вот что я пробовал:

InputStream input = new FileInputStream("data.bin");
byte[] data = ??? // Как получить массив байт?

Проблема в том, что InputStream не даёт мне заранее узнать размер данных. Метод available() возвращает не полный размер, а только то, что уже доступно для чтения без блокировки. Пытался читать в цикле, но код получается корявым и часто ломается.

Я читал про ByteArrayOutputStream, но не понимаю, как его правильно использовать. Ещё слышал про readAllBytes() в Java 9, но мой проект на Java 8. Подскажите, пожалуйста, универсальный и надёжный способ, который работает во всех версиях Java.

Важно: мне нужно получить именно массив байт, а не строку. И чтобы решение было эффективным по памяти, так как файлы могут быть большими (десятки мегабайт).

Заранее спасибо за помощь!

<<>>

Лучший способ: используем ByteArrayOutputStream (Java 7+)

Самый надёжный и универсальный способ, работающий во всех версиях Java — это чтение через ByteArrayOutputStream. Вот готовый метод:

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

public static byte[] toByteArray(InputStream input) throws IOException {
    ByteArrayOutputStream output = new ByteArrayOutputStream();
    byte[] buffer = new byte[8192]; // 8 КБ — оптимальный размер буфера
    int bytesRead;
    
    while ((bytesRead = input.read(buffer)) != -1) {
        output.write(buffer, 0, bytesRead);
    }
    
    return output.toByteArray();
}

Как это работает:

  1. Буфер фиксированного размера — мы читаем данные порциями по 8 КБ. Это не нагружает память и эффективно работает с любыми размерами файлов.
  2. ByteArrayOutputStream — динамический массив, который автоматически расширяется при записи. Внутри он использует byte[], который при необходимости увеличивается (как ArrayList).
  3. Цикл while — читаем, пока метод read() не вернёт -1 (конец потока).

Почему это лучший вариант:

  • Работает во всех версиях Java (начиная с 1.0)
  • Не требует предварительного знания размера потока
  • Эффективно использует память — буфер всего 8 КБ, а итоговый массив ровно такого размера, сколько данных
  • Подходит для потоков любого типа: файловые, сетевые, из памяти

Для Java 9 и новее (если можете обновиться):

byte[] data = input.readAllBytes(); // Одна строка!

Метод readAllBytes() делает то же самое, но внутри оптимизирован — он использует массив с начальной ёмкостью из available(), что может быть чуть быстрее.

Важные моменты:

  • Всегда закрывайте поток после чтения (используйте try-with-resources в Java 7+)
  • Для очень больших файлов (сотни мегабайт) лучше не загружать всё в память — обрабатывайте данные по частям
  • Если поток уже содержит все данные (например, ByteArrayInputStream), можно просто вызвать input.readAllBytes() или использовать input.available() для предварительного выделения массива

Этот метод проверен годами и используется в стандартных библиотеках (например, Apache Commons IO — класс IOUtils.toByteArray()). Можете смело применять его в своих проектах!

<<>>

Альтернатива: используем Apache Commons IO

Если в вашем проекте уже используется библиотека Apache Commons IO, то задача решается одной строкой:

import org.apache.commons.io.IOUtils;

byte[] data = IOUtils.toByteArray(inputStream);

Внутри этот метод делает то же самое, что и наш ручной вариант — использует ByteArrayOutputStream с буфером 4 КБ. Преимущество в том, что код становится чище, и вам не нужно писать утилитный метод самому.

Когда стоит использовать:

  • В проектах, где уже есть Commons IO (например, через Maven/Gradle)
  • Когда нужно часто читать потоки в разных местах
  • Для быстрого прототипирования

Минусы:

  • Добавляет зависимость (~300 КБ)
  • Незначительно медленнее ручного метода (из-за дополнительных проверок)
  • Не подходит, если вы хотите минимизировать внешние библиотеки

Если вы не используете Commons IO, рекомендую первый вариант — он простой и не требует зависимостей. Но если библиотека уже подключена — смело пользуйтесь IOUtils, это стандарт де-факто для работы с потоками в Java.