Преобразовать InputStream в байтовый массив в Java
Как мне прочитать весь InputStream
в байтовый массив?
36 ответов
Вы можете использовать Apache Commons IO для решения этой и подобных задач.
IOUtils
Тип имеет статический метод для чтения InputStream
и вернуть byte[]
,
InputStream is;
byte[] bytes = IOUtils.toByteArray(is);
Внутренне это создает ByteArrayOutputStream
и копирует байты на выход, затем вызывает toByteArray()
, Он обрабатывает большие файлы, копируя байты в блоки по 4 КБ.
Вам нужно прочитать каждый байт из вашего 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();
Наконец, через двадцать лет, благодаря Java 9, существует простое решение без необходимости использования сторонней библиотеки:
InputStream is;
…
byte[] array = is.readAllBytes();
Обратите внимание также на удобство методов readNBytes(byte[] b, int off, int len)
а также transferTo(OutputStream)
удовлетворение повторяющихся потребностей.
Используйте ванильную Java DataInputStream
И его readFully
Метод (существует, по крайней мере, с Java 1.4):
...
byte[] bytes = new byte[(int) file.length()];
DataInputStream dis = new DataInputStream(new FileInputStream(file));
dis.readFully(bytes);
...
У этого метода есть и другие варианты, но я все время использую его для этого варианта использования.
Если вы используете Google Guava, это будет так просто, как:
byte[] bytes = ByteStreams.toByteArray(inputStream);
Безопасное решение (с возможностью 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. < 9):
@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
смотрите здесь.
Как всегда, в Spring Framework (spring-core начиная с 3.2.2) есть что-то для вас: StreamUtils.copyToByteArray()
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();
}
В случае, если кто-то все еще ищет решение без зависимости и если у вас есть файл.
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);
Вам действительно нужно изображение как byte[]
? Что именно вы ожидаете в byte[]
- полное содержимое файла изображения, закодированное в любом формате, в котором находится файл изображения, или значения пикселей RGB?
Другие ответы здесь показывают, как прочитать файл в byte[]
, Ваш byte[]
будет содержать точное содержимое файла, и вам нужно будет декодировать это, чтобы сделать что-нибудь с данными изображения.
Стандартный API Java для чтения (и записи) изображений - это API ImageIO, который вы можете найти в пакете. 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).
Смотрите также следующий учебник: Работа с изображениями
Если вы не хотите использовать библиотеку Apache 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's 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;
}
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();
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();
@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;
}
Java 9 даст вам, наконец, хороший метод:
InputStream in = ...;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
in.transferTo( bos );
byte[] bytes = bos.toByteArray();
Мы наблюдаем некоторую задержку для нескольких транзакций 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
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') и может быть неуместным.
Я знаю, что уже слишком поздно, но я думаю, что это более чистое решение, которое более читабельно...
/**
* 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();
}
Java 7 и более поздние версии:
import sun.misc.IOUtils;
...
InputStream in = ...;
byte[] buf = IOUtils.readFully(in, -1, false);
Я попытался отредактировать ответ @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();
Кстати, ByteArrayOutputStream не нужно закрывать. конструкции try/finally опущены для удобства чтения
Оберните его в 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;
}
Увидеть InputStream.available()
документация:
Особенно важно понимать, что вы не должны использовать этот метод для определения размера контейнера и предположить, что вы можете прочитать весь поток без необходимости изменять размер контейнера. Такие вызывающие абоненты, вероятно, должны записывать все, что они читают, в ByteArrayOutputStream и преобразовывать это в байтовый массив. В качестве альтернативы, если вы читаете из файла, File.length возвращает текущую длину файла (хотя допущение, что длина файла не может быть изменена, может быть неправильным, чтение файла по своей природе является довольно редким).
Другой случай получить правильный байтовый массив через поток, после отправки запроса на сервер и ожидания ответа.
/**
* 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);
Это моя версия для копирования и вставки:
@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();
}
Я использую это.
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();
}
}
Вы можете использовать библиотеку cactoos с многоразовыми объектно-ориентированными компонентами Java. Эта библиотека делает упор на ООП, поэтому никаких статических методов, значений NULL и т. Д., Только реальные объекты и их контракты (интерфейсы). Простая операция, такая как чтение InputStream, может быть выполнена так
final InputStream input = ...;
final Bytes bytes = new BytesOf(input);
final byte[] array = bytes.asBytes();
Assert.assertArrayEquals(
array,
new byte[]{65, 66, 67}
);
Имея выделенный тип Bytes
для работы со структурой данных byte[]
позволяет использовать тактику ООП для решения поставленных задач. То, что нам запретит процедурный "служебный" метод. Например, вам нужно объединить байты, которые вы прочитали из этогоInputStream
в Base64. В этом случае вы будете использовать шаблон Decorator и обернуть объект Bytes в реализации для Base64.cactoos уже предоставляет такую реализацию:
final Bytes encoded = new BytesBase64(
new BytesOf(
new InputStreamOf("XYZ")
)
);
Assert.assertEquals(new TextOf(encoded).asString(), "WFla");
Вы можете декодировать их таким же образом, используя шаблон Decorator.
final Bytes decoded = new Base64Bytes(
new BytesBase64(
new BytesOf(
new InputStreamOf("XYZ")
)
)
);
Assert.assertEquals(new TextOf(decoded).asString(), "XYZ");
Какой бы ни была ваша задача, вы сможете создать собственную реализацию Bytes
чтобы решить это.
Решение на Kotlin (конечно, будет работать и на Java), которое включает оба случая, когда вы знаете размер или нет:
fun InputStream.readBytesWithSize(size: Long): ByteArray? {
return when {
size < 0L -> this.readBytes()
size == 0L -> ByteArray(0)
size > Int.MAX_VALUE -> null
else -> {
val sizeInt = size.toInt()
val result = ByteArray(sizeInt)
readBytesIntoByteArray(result, sizeInt)
result
}
}
}
fun InputStream.readBytesIntoByteArray(byteArray: ByteArray,bytesToRead:Int=byteArray.size) {
var offset = 0
while (true) {
val read = this.read(byteArray, offset, bytesToRead - offset)
if (read == -1)
break
offset += read
if (offset >= bytesToRead)
break
}
}
Если вы знаете размер, это сэкономит вам вдвое больше памяти по сравнению с другими решениями (на короткое время, но все же может быть полезно). Это потому, что вам нужно прочитать весь поток до конца, а затем преобразовать его в массив байтов (аналогично ArrayList, который вы преобразовываете только в массив).
Итак, если вы, например, используете Android и у вас есть Uri для обработки, вы можете попытаться получить размер, используя это:
fun getStreamLengthFromUri(context: Context, uri: Uri): Long {
context.contentResolver.query(uri, arrayOf(MediaStore.MediaColumns.SIZE), null, null, null)?.use {
if (!it.moveToNext())
return@use
val fileSize = it.getLong(it.getColumnIndex(MediaStore.MediaColumns.SIZE))
if (fileSize > 0)
return fileSize
}
//if you wish, you can also get the file-path from the uri here, and then try to get its size, using this: https://stackru.com/a/61835665/878126
FileUtilEx.getFilePathFromUri(context, uri, false)?.use {
val file = it.file
val fileSize = file.length()
if (fileSize > 0)
return fileSize
}
context.contentResolver.openInputStream(uri)?.use { inputStream ->
if (inputStream is FileInputStream)
return inputStream.channel.size()
else {
var bytesCount = 0L
while (true) {
val available = inputStream.available()
if (available == 0)
break
val skip = inputStream.skip(available.toLong())
if (skip < 0)
break
bytesCount += skip
}
if (bytesCount > 0L)
return bytesCount
}
}
return -1L
}
Вы делаете дополнительную копию, если используете 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);
Обратите внимание, что последняя строка выше относится к файлам, усеченным во время чтения потока, если вам нужно обработать эту возможность, но если файл становится длиннее во время чтения потока, содержимое массива byte[] не будет удлиняться чтобы включить новое содержимое файла, массив будет просто урезан до старой длины inputStreamLength.
Вот оптимизированная версия, которая старается максимально избежать копирования байтов данных:
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;
}
}