Преобразовать InputStream в байтовый массив в Java

725

Как я могу прочитать весь InputStream в массив байтов?

Теги:
inputstream
bytearray

31 ответ

1037
Лучший ответ

Вы можете использовать Apache Commons IO для решения этой и подобных задач.

Тип IOUtils имеет статический метод для чтения InputStream и возвращает a byte[].

InputStream is;
byte[] bytes = IOUtils.toByteArray(is);

Внутри это создает ByteArrayOutputStream и копирует байты на вывод, а затем вызывает toByteArray(). Он обрабатывает большие файлы, копируя байты в блоках 4KiB.

  • 177
    Если вы хотите написать 4 строки кода, считаете ли вы целесообразным импортировать сторонние зависимости?
  • 199
    Если есть библиотека, которая обрабатывает требование и занимается обработкой больших файлов и хорошо протестирована, то, конечно, вопрос в том, зачем мне ее писать? Jar всего 107KB, и если вам нужен один метод из этого, вы, вероятно, будете использовать и другие
Показать ещё 20 комментариев
388

Вам нужно прочитать каждый байт из вашего InputStream и записать его в ByteArrayOutputStream. Затем вы можете получить базовый байтовый массив, вызвав toByteArray(); например

InputStream is = ...
ByteArrayOutputStream buffer = new ByteArrayOutputStream();

int nRead;
byte[] data = new byte[16384];

while ((nRead = is.read(data, 0, data.length)) != -1) {
  buffer.write(data, 0, nRead);
}

return buffer.toByteArray();
  • 14
    Как насчет размера вновь созданного байта []. Почему это 16384? Как я могу определить точный правильный размер? Большое спасибо.
  • 6
    16384 - довольно произвольный выбор, хотя я склоняюсь к степени 2, чтобы увеличить вероятность выравнивания массива по границам слов. Ответ pihentagy показывает, как можно избежать использования промежуточного буфера, а лучше выделить массив правильного размера. Если вы не имеете дело с большими файлами, я лично предпочитаю приведенный выше код, который является более элегантным и может использоваться для InputStreams, где число байтов для чтения заранее неизвестно.
Показать ещё 16 комментариев
223

Наконец, через двадцать лет theres простое решение без необходимости в сторонней библиотеке благодаря Java 9:

InputStream is;
…
byte[] array = is.readAllBytes();

Обратите внимание также на методы удобства readNBytes(byte[] b, int off, int len) и transferTo(OutputStream) для удовлетворения повторяющихся потребностей.

  • 8
    ... и readNBytes(int len) в Java11.
  • 0
    И еще через 20 лет государственное агентство, в котором я работаю, могло бы перейти на Java 9 ...
116

Если вы используете google guava, это будет так же просто, как:

byte[] bytes = ByteStreams.toByteArray(inputStream);
  • 4
    ByteStreams аннотируется @Beta
109

Используйте ванильный Java DataInputStream и его метод readFully (существует, по крайней мере, с Java 1.4):

...
byte[] bytes = new byte[(int) file.length()];
DataInputStream dis = new DataInputStream(new FileInputStream(file));
dis.readFully(bytes);
...

У этого метода есть и другие варианты, но я все время использую его для этого варианта использования.

  • 39
    +1 за использование стандартных библиотек вместо сторонней зависимости. К сожалению, это не работает для меня, потому что я не знаю длину потока заранее.
  • 2
    что такое imgFile? Это не может быть InputStream, который должен был быть вводом этого метода
Показать ещё 10 комментариев
34
public static byte[] getBytesFromInputStream(InputStream is) throws IOException {
    ByteArrayOutputStream os = new ByteArrayOutputStream(); 
    byte[] buffer = new byte[0xFFFF];
    for (int len = is.read(buffer); len != -1; len = is.read(buffer)) { 
        os.write(buffer, 0, len);
    }
    return os.toByteArray();
}
  • 2
    Это пример, и, как таковая, краткость - это порядок дня. Также возвращение null здесь будет правильным выбором в некоторых случаях (хотя в производственной среде у вас также будет надлежащая обработка исключений и документация).
  • 9
    Я понимаю краткость в примере, но почему бы просто не заставить метод примера генерировать IOException, а не проглотить его и вернуть бессмысленное значение?
Показать ещё 2 комментария
30

Как всегда, Spring framework (spring -core с 3.2.2) имеет что-то для вас: StreamUtils.copyToByteArray()

  • 0
    Как и большинство других, я хотел избежать использования сторонней библиотеки для чего-то такого простого, но Java 9 на данный момент не подходит ... к счастью, я уже использовал Spring.
19

Вам действительно нужно изображение как byte[]? Что именно вы ожидаете от byte[] - полного содержимого файла изображения, закодированного в любом формате, в котором находится файл изображения, или значений пикселей RGB?

Другие ответы здесь показывают вам, как читать файл в byte[]. Ваш byte[] будет содержать точное содержимое файла, и вам нужно будет декодировать это, чтобы что-либо сделать с данными изображения.

Java стандартный API для чтения (и записи) изображений - это ImageIO API, который вы можете найти в пакете javax.imageio. Вы можете читать изображение из файла только с одной строкой кода:

BufferedImage image = ImageIO.read(new File("image.jpg"));

Это даст вам BufferedImage, а не byte[]. Чтобы получить данные изображения, вы можете вызвать getRaster() на BufferedImage. Это даст вам объект Raster, который имеет методы для доступа к данным пикселя (он имеет несколько методов getPixel()/getPixels()).

Посмотрите документацию API для javax.imageio.ImageIO, java.awt.image.BufferedImage, java.awt.image.Raster и т.д.

ImageIO поддерживает несколько форматов изображений по умолчанию: JPEG, PNG, BMP, WBMP и GIF. Можно добавить поддержку для большего количества форматов (вам понадобится плагин, который реализует интерфейс поставщика услуг ImageIO).

См. также следующий учебник: Работа с изображениями

14

Если вы не хотите использовать библиотеку Apocal commons-io, этот фрагмент взят из класса sun.misc.IOUtils. Это почти в два раза быстрее, чем обычная реализация с использованием ByteBuffers:

public static byte[] readFully(InputStream is, int length, boolean readAll)
        throws IOException {
    byte[] output = {};
    if (length == -1) length = Integer.MAX_VALUE;
    int pos = 0;
    while (pos < length) {
        int bytesToRead;
        if (pos >= output.length) { // Only expand when there no room
            bytesToRead = Math.min(length - pos, output.length + 1024);
            if (output.length < pos + bytesToRead) {
                output = Arrays.copyOf(output, pos + bytesToRead);
            }
        } else {
            bytesToRead = output.length - pos;
        }
        int cc = is.read(output, pos, bytesToRead);
        if (cc < 0) {
            if (readAll && length != Integer.MAX_VALUE) {
                throw new EOFException("Detect premature EOF");
            } else {
                if (output.length != pos) {
                    output = Arrays.copyOf(output, pos);
                }
                break;
            }
        }
        pos += cc;
    }
    return output;
}
  • 0
    Это немного странное решение, длина - это верхняя граница длины массива. Если вы знаете длину, вам нужно только: byte [] output = new byte [length]; is.read (выход); (но смотри мой ответ)
  • 0
    @ luke-hutchison, как я уже сказал, это решение sun.misc.IOUtils. В наиболее распространенных случаях вы не знаете размер входного потока InputStream, поэтому if (length == -1) length = Integer.MAX_VALUE; применяется. Это решение работает, даже если заданная длина больше, чем длина InputStream.
Показать ещё 1 комментарий
12

В случае, если кто-то все еще ищет решение без зависимости и если у вас есть файл.

1) DataInputStream

 byte[] data = new byte[(int) file.length()];
 DataInputStream dis = new DataInputStream(new FileInputStream(file));
 dis.readFully(data);
 dis.close();

2) ByteArrayOutputStream

 InputStream is = new FileInputStream(file);
 ByteArrayOutputStream buffer = new ByteArrayOutputStream();
 int nRead;
 byte[] data = new byte[(int) file.length()];
 while ((nRead = is.read(data, 0, data.length)) != -1) {
     buffer.write(data, 0, nRead);
 }

3) RandomAccessFile

 RandomAccessFile raf = new RandomAccessFile(file, "r");
 byte[] data = new byte[(int) raf.length()];
 raf.readFully(data);
  • 2
    или просто: byte [] data = Files.readAllBytes (file.toPath ());
9
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
while (true) {
    int r = in.read(buffer);
    if (r == -1) break;
    out.write(buffer, 0, r);
}

byte[] ret = out.toByteArray();
8

@Adamski: вы можете полностью исключить буфер.

Код скопирован из http://www.exampledepot.com/egs/java.io/File2ByteArray.html (Да, он очень многословный, но в качестве другого решения требуется половина объема памяти.)

// Returns the contents of the file in a byte array.
public static byte[] getBytesFromFile(File file) throws IOException {
    InputStream is = new FileInputStream(file);

    // Get the size of the file
    long length = file.length();

    // You cannot create an array using a long type.
    // It needs to be an int type.
    // Before converting to an int type, check
    // to ensure that file is not larger than Integer.MAX_VALUE.
    if (length > Integer.MAX_VALUE) {
        // File is too large
    }

    // Create the byte array to hold the data
    byte[] bytes = new byte[(int)length];

    // Read in the bytes
    int offset = 0;
    int numRead = 0;
    while (offset < bytes.length
           && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
        offset += numRead;
    }

    // Ensure all the bytes have been read in
    if (offset < bytes.length) {
        throw new IOException("Could not completely read file "+file.getName());
    }

    // Close the input stream and return bytes
    is.close();
    return bytes;
}
  • 5
    Зависит от знания размера заранее.
  • 2
    Конечно, но они должны знать размер: «Я хочу прочитать изображение»
Показать ещё 4 комментария
7
Input Stream is ...
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int next = in.read();
while (next > -1) {
    bos.write(next);
    next = in.read();
}
bos.flush();
byte[] result = bos.toByteArray();
bos.close();
  • 22
    Один и один байт может быть убийцей производительности. Серьезно.
  • 0
    Однако, как правило, операционная система уже достаточно буферизирует, чтобы не беспокоиться о файлах меньшего размера. Не то, чтобы головка жесткого диска считывала каждый байт отдельно (жесткий диск - это вращающаяся стеклянная пластинка с магнитной кодированной информацией, немного похожая на странный значок, который мы используем для сохранения данных: P).
Показать ещё 1 комментарий
3

Java 9 даст вам наконец хороший метод:

InputStream in = ...;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
in.transferTo( bos );
byte[] bytes = bos.toByteArray();
  • 3
    В чем разница между этим и InputStram.readAllBytes() который является однострочным?
2

Безопасное решение (с возможностью корректного close потоков):

  • Версия Java 9+:

    final byte[] bytes;
    try (inputStream) {
        bytes = inputStream.readAllBytes();
    }
    
  • Версия Java 8:

    public static byte[] readAllBytes(InputStream inputStream) throws IOException {
        final int bufLen = 4 * 0x400; // 4KB
        byte[] buf = new byte[bufLen];
        int readLen;
        IOException exception = null;
    
        try {
            try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
                while ((readLen = inputStream.read(buf, 0, bufLen)) != -1)
                    outputStream.write(buf, 0, readLen);
    
                return outputStream.toByteArray();
            }
        } catch (IOException e) {
            exception = e;
            throw e;
        } finally {
            if (exception == null) inputStream.close();
            else try {
                inputStream.close();
            } catch (IOException e) {
                exception.addSuppressed(e);
            }
        }
    }
    
  • Версия Kotlin (совместимая с Java v ≤ 8):

    @Throws(IOException::class)
    fun InputStream.readAllBytes(): ByteArray {
        val bufLen = 4 * 0x400 // 4KB
        val buf = ByteArray(bufLen)
        var readLen: Int = 0
    
        ByteArrayOutputStream().use { o ->
            this.use { i ->
                while (i.read(buf, 0, bufLen).also { readLen = it } != -1)
                    o.write(buf, 0, readLen)
            }
    
            return o.toByteArray()
        }
    }
    

    Чтобы избежать вложенного use см. Здесь.

2

Я знаю это слишком поздно, но здесь я думаю, что это более чистое решение, которое более читаемо...

/**
 * method converts {@link InputStream} Object into byte[] array.
 * 
 * @param stream the {@link InputStream} Object.
 * @return the byte[] array representation of received {@link InputStream} Object.
 * @throws IOException if an error occurs.
 */
public static byte[] streamToByteArray(InputStream stream) throws IOException {

    byte[] buffer = new byte[1024];
    ByteArrayOutputStream os = new ByteArrayOutputStream();

    int line = 0;
    // read bytes from stream, and store them in buffer
    while ((line = stream.read(buffer)) != -1) {
        // Writes bytes from byte array (buffer) into output stream.
        os.write(buffer, 0, line);
    }
    stream.close();
    os.flush();
    os.close();
    return os.toByteArray();
}
  • 2
    Вы должны использовать try-with-resources.
  • 0
    Приведение в порядок в конце должно выполняться в блоке finally в случае ошибок, иначе это может привести к утечке памяти.
1

Java 8-way (благодаря BufferedReader и Adam Bien)

private static byte[] readFully(InputStream input) throws IOException {
    try (BufferedReader buffer = new BufferedReader(new InputStreamReader(input))) {
        return buffer.lines().collect(Collectors.joining("\n")).getBytes(<charset_can_be_specified>);
    }
}

Примечание, что это решение вытирает возврат каретки ('\ r') и может быть неуместным.

  • 3
    Это для String . ОП запрашивает byte[] .
  • 0
    @ FrozenFire спасибо, я обновил ответ
Показать ещё 2 комментария
1

Java 7 и более поздние версии:

import sun.misc.IOUtils;
...
InputStream in = ...;
byte[] buf = IOUtils.readFully(in, -1, false);
  • 17
    sun.misc.IOUtils - это не «Java 7». Это собственный, специфичный для реализации класс, который может отсутствовать в других реализациях JRE и может исчезнуть без предупреждения в одном из следующих выпусков.
1

См. документацию InputStream.available():

Особенно важно понимать, что вы не должны использовать это метод для определения размера контейнера и предположим, что вы можете прочитать все потока без необходимости изменения размера контейнера. Такие абоненты должен, вероятно, написать все, что они читают, в ByteArrayOutputStream и преобразовать это в массив байтов. Кроме того, если вы читаете из файла File.length возвращает текущую длину файла (хотя предполагается, что длина файла не может быть изменена, может быть неправильной, чтение файла по своей природе является racy).

1

Я попытался отредактировать ответ @numan с исправлением для записи данных мусора, но изменение было отклонено. Хотя этот короткий фрагмент кода ничего блестящего, я не вижу другого лучшего ответа. Вот что для меня имеет смысл:

ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024]; // you can configure the buffer size
int length;

while ((length = in.read(buffer)) != -1) out.write(buffer, 0, length); //copy streams
in.close(); // call this in a finally block

byte[] result = out.toByteArray();

btw ByteArrayOutputStream не нужно закрывать. try/finally конструкции опущены для удобочитаемости

0

Мы наблюдаем некоторую задержку для нескольких транзакций AWS при преобразовании объекта S3 в ByteArray.

Примечание. Объект S3 - это документ PDF (максимальный размер - 3 МБ).

Мы используем параметр # 1 (org.apache.commons.io.IOUtils) для преобразования объекта S3 в ByteArray. Мы заметили, что S3 предоставляет встроенный метод IOUtils для преобразования объекта S3 в ByteArray, мы просим вас подтвердить наилучший способ преобразования объекта S3 в ByteArray, чтобы избежать задержки.

Опция 1:

import org.apache.commons.io.IOUtils;
is = s3object.getObjectContent();
content =IOUtils.toByteArray(is);

Вариант № 2:

import com.amazonaws.util.IOUtils;
is = s3object.getObjectContent();
content =IOUtils.toByteArray(is);

Также дайте мне знать, если у нас есть какой-либо другой лучший способ преобразовать объект s3 в bytearray

0

Вы можете попробовать Cactoos:

byte[] array = new BytesOf(stream).bytes();
0

Оберните его в DataInputStream, если это почему-то отсутствует в таблице, просто используйте read, чтобы забить его, пока он не даст вам -1 или весь запрошенный вами блок.

public int readFully(InputStream in, byte[] data) throws IOException {
    int offset = 0;
    int bytesRead;
    boolean read = false;
    while ((bytesRead = in.read(data, offset, data.length - offset)) != -1) {
        read = true;
        offset += bytesRead;
        if (offset >= data.length) {
            break;
        }
    }
    return (read) ? offset : -1;
}
0

Это моя версия для копирования-вставки:

@SuppressWarnings("empty-statement")
public static byte[] inputStreamToByte(InputStream is) throws IOException {
    if (is == null) {
        return null;
    }
    // Define a size if you have an idea of it.
    ByteArrayOutputStream r = new ByteArrayOutputStream(2048);
    byte[] read = new byte[512]; // Your buffer size.
    for (int i; -1 != (i = is.read(read)); r.write(read, 0, i));
    is.close();
    return r.toByteArray();
}
  • 2
    Хотя этот фрагмент кода может решить вопрос, в том числе объяснение действительно помогает улучшить качество вашего сообщения. Помните, что вы отвечаете на вопрос читателей в будущем, и эти люди могут не знать причин, по которым вы предлагаете код.
0

Я использую это.

public static byte[] toByteArray(InputStream is) throws IOException {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        try {
            byte[] b = new byte[4096];
            int n = 0;
            while ((n = is.read(b)) != -1) {
                output.write(b, 0, n);
            }
            return output.toByteArray();
        } finally {
            output.close();
        }
    }
  • 2
    Добавьте некоторое объяснение с ответом о том, как этот ответ поможет ОП в устранении текущей проблемы.
0

Вы делаете дополнительную копию, если используете ByteArrayOutputStream. Если вы знаете длину потока, прежде чем начать его читать (например, InputStream на самом деле является FileInputStream, и вы можете вызвать file.length() в файле, или InputStream - это запись в zipfile InputStream, и вы можете вызвать zipEntry. length()), то гораздо лучше записать непосредственно в массив byte [] - он использует половину памяти и экономит время.

// Read the file contents into a byte[] array
byte[] buf = new byte[inputStreamLength];
int bytesRead = Math.max(0, inputStream.read(buf));

// If needed: for safety, truncate the array if the file may somehow get
// truncated during the read operation
byte[] contents = bytesRead == inputStreamLength ? buf
                  : Arrays.copyOf(buf, bytesRead);

N.B. в последней строке выше рассматриваются файлы, которые усекаются во время чтения потока, если вам нужно обработать эту возможность, но если файл становится длиннее при чтении потока, содержимое в массиве byte [] не будет увеличено до включить новое содержимое файла, массив просто будет усечен до старой длины inputStreamLength.

0

Другой случай, чтобы получить правильный массив байтов через поток, после отправки запроса на сервер и ожидания ответа.

/**
         * Begin setup TCP connection to PC app
         * to open integrate connection between mobile app and pc app (or mobile app)
         */
        mSocket = new Socket(IP, port);
       // mSocket.setSoTimeout(30000);

        DataOutputStream mDos = new DataOutputStream(mSocket.getOutputStream());

        String str = "MobileRequest#" + params[0] + "#<EOF>";

        mDos.write(str.getBytes());

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        /* Since data are accepted as byte, all of them will be collected in the
        following byte array which initialised with accepted data length. */
        DataInputStream mDis = new DataInputStream(mSocket.getInputStream());
        byte[] data = new byte[mDis.available()];

        // Collecting data into byte array
        for (int i = 0; i < data.length; i++)
            data[i] = mDis.readByte();

        // Converting collected data in byte array into String.
        String RESPONSE = new String(data);
0

Вот оптимизированная версия, которая старается максимально избежать копирования байтов данных:

private static byte[] loadStream (InputStream stream) throws IOException {
   int available = stream.available();
   int expectedSize = available > 0 ? available : -1;
   return loadStream(stream, expectedSize);
}

private static byte[] loadStream (InputStream stream, int expectedSize) throws IOException {
   int basicBufferSize = 0x4000;
   int initialBufferSize = (expectedSize >= 0) ? expectedSize : basicBufferSize;
   byte[] buf = new byte[initialBufferSize];
   int pos = 0;
   while (true) {
      if (pos == buf.length) {
         int readAhead = -1;
         if (pos == expectedSize) {
            readAhead = stream.read();       // test whether EOF is at expectedSize
            if (readAhead == -1) {
               return buf;
            }
         }
         int newBufferSize = Math.max(2 * buf.length, basicBufferSize);
         buf = Arrays.copyOf(buf, newBufferSize);
         if (readAhead != -1) {
            buf[pos++] = (byte)readAhead;
         }
      }
      int len = stream.read(buf, pos, buf.length - pos);
      if (len < 0) {
         return Arrays.copyOf(buf, pos);
      }
      pos += len;
   }
}
-1
/*InputStream class_InputStream = null;
I am reading class from DB 
class_InputStream = rs.getBinaryStream(1);
Your Input stream could be from any source
*/
int thisLine;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while ((thisLine = class_InputStream.read()) != -1) {
    bos.write(thisLine);
}
bos.flush();
byte [] yourBytes = bos.toByteArray();

/*Don't forget in the finally block to close ByteArrayOutputStream & InputStream
 In my case the IS is from resultset so just closing the rs will do it*/

if (bos != null){
    bos.close();
}
  • 0
    Закрытие и сброс босов - пустая трата щелчков клавиатуры. Закрытие входного потока, скорее всего, поможет. Чтение одного байта за раз неэффективно. Смотрите ответ Нуман.
-2

Это работает для меня,

if(inputStream != null){
                ByteArrayOutputStream contentStream = readSourceContent(inputStream);
                String stringContent = contentStream.toString();
                byte[] byteArr = encodeString(stringContent);
            }

readSourceContent()

public static ByteArrayOutputStream readSourceContent(InputStream inputStream) throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        int nextChar;
        try {
            while ((nextChar = inputStream.read()) != -1) {
                outputStream.write(nextChar);
            }
            outputStream.flush();
        } catch (IOException e) {
            throw new IOException("Exception occurred while reading content", e);
        }

        return outputStream;
    }

encodeString()

public static byte[] encodeString(String content) throws UnsupportedEncodingException {
        byte[] bytes;
        try {
            bytes = content.getBytes();

        } catch (UnsupportedEncodingException e) {
            String msg = ENCODING + " is unsupported encoding type";
            log.error(msg,e);
            throw new UnsupportedEncodingException(msg, e);
        }
        return bytes;
    }
  • 0
    Зачем преобразовывать в String а затем в byte[] (который может связываться с двоичными данными)? ByteArrayOutputStream имеет .toByteArray() : docs.oracle.com/javase/7/docs/api/java/io/…
-2

Ниже кодов

public static byte[] serializeObj(Object obj) throws IOException {
  ByteArrayOutputStream baOStream = new ByteArrayOutputStream();
  ObjectOutputStream objOStream = new ObjectOutputStream(baOStream);

  objOStream.writeObject(obj); 
  objOStream.flush();
  objOStream.close();
  return baOStream.toByteArray(); 
} 

ИЛИ

BufferedImage img = ...
ByteArrayOutputStream baos = new ByteArrayOutputStream(1000);
ImageIO.write(img, "jpeg", baos);
baos.flush();
byte[] result = baos.toByteArray();
baos.close();

Ещё вопросы

Сообщество Overcoder
Наверх
Меню