Перетворити InputStream в байтовий масив на Java


Відповіді:


1135

Ви можете використовувати AOche Commons IO для вирішення цієї та подібних завдань.

IOUtilsТип має статичний метод , щоб прочитати InputStreamі повертав byte[].

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

Внутрішньо це створює a ByteArrayOutputStreamі копіює байти на вихід, а потім викликає toByteArray(). Він обробляє великі файли, копіюючи байти в блоки розміром 4 КБ.


188
Хочете написати 4 рядки коду, чи вважаєте ви, що імпорт сторонньої залежності варто?
oxbow_lakes

217
Якщо є бібліотека, яка обробляє цю вимогу і займається обробкою великих файлів, і вона добре перевірена, то, безумовно, питання полягає в тому, чому я б писав її сам? Баночка становить всього 107 Кб, і якщо вам потрібен один метод з неї, ви, ймовірно, будете використовувати й інші
Rich Seller

242
@oxbow_lakes: враховуючи приголомшливу кількість неправильних реалізацій цієї функції, які я бачив у своєму житті розробника, я вважаю, що так, це дуже варте зовнішньої залежності, щоб виправити це.
Йоахім Зауер

17
Чому б не піти і подивитись такі речі, як Apache, FastArrayListабо їхні "слабкі та слабкі" посилання "Карти", і повернутися, щоб розповісти, наскільки "добре перевірена" ця бібліотека. Це купа сміття
oxbow_lakes

87
Окрім Apache commons-io, ознайомтесь із класом ByteStreams від Google Guava . InputStream is; byte[] filedata=ByteStreams.toByteArray(is);
michaelok

446

Вам потрібно прочитати кожен байт зі свого 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();

16
Як щодо розміру новоствореного байта []. Чому це 16384? Як я міг визначити точний правильний розмір? Дуже дякую.
Ондрей Божек

6
16384 - це досить довільний вибір, хоча я схильний надавати переваги 2, щоб збільшити шанс вирівнювання масиву з межами слова. Відповідь pihentagy показує, як можна уникнути використання проміжного буфера, а краще виділити масив правильного розміру. Якщо ви не маєте справу з великими файлами, я особисто віддаю перевагу наведеному вище коду, який є більш елегантним і його можна використовувати для InputStreams, де кількість байтів для читання заздалегідь не відома.
Адамські

@Adamski Не створює масив байтів набагато більше, ніж ви очікували, що дані будуть в потоці, витрачайте пам'ять?
Paul Brewczynski

@bluesm: Так, це правильно. Однак у моєму прикладі байтовий масив становить лише 16 Кбіт і настільки крихітний за сьогоднішніми стандартами. Також, звичайно, ця пам'ять після цього знову буде звільнена.
Адамські

5
@Adamski Дуже багато інфраструктурних апаратних засобів, веб-серверів та компонентів ОС використовують 4K буфери для переміщення даних, тому це причина точної кількості, але головне в тому, що ви отримуєте таке невелике підвищення продуктивності, перейшовши на 4K що це взагалі вважається марною пам’яттю. Я припускаю, що це все- таки правда, тому що це знання, які я мав десятиліття!


132

Використовуйте ванільний Java DataInputStreamта його readFullyметод (існує, принаймні, Java 1.4):

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

Є деякі інші аромати цього способу, але я використовую це весь час для цього випадку використання.


45
+1 для використання стандартних бібліотек замість сторонньої залежності. На жаль, це не працює для мене, тому що я не знаю довжини потоку вперед.
Ендрю Спенсер

2
що таке imgFile? Це не може бути InputStream, який повинен був стати вхідним методом
Janus Troelsen

4
@janus це "Файл". цей спосіб працює лише у тому випадку, якщо ви знаєте довжину файлу або кількість байтів для читання.
дерморіц

5
Цікава річ, але ви повинні знати точну довжину (частини потоку) для читання. Більше того, клас DataInputStreamвикористовується в основному для зчитування первинних типів (Longs, Shorts, Chars ...) з потоку, тому ми можемо сприймати це використання як неправильне використання класу.
Олів'є Фошо

17
Якщо ви вже знаєте довжину даних для читання з потоку, це не краще, ніж InputStream.read.
Логан Пікап

119

Якщо ви випадково використовуєте google guava , це буде так само просто:

byte[] bytes = ByteStreams.toByteArray(inputStream);

8
ByteStreamsпозначається повідомленням@Beta
Kid101

46

Як завжди, також Spring Framework ( Spring -core з 3.2.2) має щось для вас:StreamUtils.copyToByteArray()


Як і більшість інших, я хотів уникнути використання сторонньої бібліотеки для чогось такого простого, але на даний момент Java 9 - це не варіант ... на щастя, я вже використовував Spring.
Скоттісей

42
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
Це приклад, і як такою стислість - це порядок дня. Також повернення нуля тут буде правильним вибором у деяких випадках (хоча у виробничому середовищі ви також матимете належну обробку винятків та документацію).

11
Я розумію стислість на прикладі, але чому б не просто змусити приклад метод кинути IOException, а не проковтнути його і повернути безглузде значення?
пендор

4
я взяв на себе можливість змінити з "return null" на "кинути IOException"
kritzikratzi

3
Спробуйте з ресурсами тут не потрібні, оскільки ByteArrayOutputStream # close () нічого не робить. (ByteArrayOutputStream # flush () не потрібен і теж нічого не робить.)
Люк Хатчісон

25

Безпечне рішення (з вмінням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);
            }
        }
    }
  • Версія Котліна (коли Java 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дивіться тут .


Чи не означає це, що в якийсь момент ви мали б подвоїти використану пам'ять, оскільки у вас є і буфер, і байтовий масив? Чи не існує способу направити байти безпосередньо до масиву вихідних байтів?
андроїд розробник

@androiddeveloper; Мені шкода. Я не знаю відповіді! Але я не думаю, що так. Я думаю, що цей спосіб (використовуючи буфер) є оптимізованим способом.
Мір-Ісмаїлі

Я перевірив і це робить, але здається, що це єдине рішення, яке ви можете вибрати, коли не знаєте розміру. Якщо ви вже знаєте розмір, можете безпосередньо створити байтовий масив із заданим розміром і заповнити його. Отже, ви використовуєте функцію, яка отримає параметр розміру байту, і якщо він дійсний, використовуйте його для прямого створення та заповнення масиву байтів, не створюючи жодного іншого великого об'єкта.
андроїд розробник

@androiddeveloper; Дякуємо за інформацію! Я їх не знав.
Мір-Ісмаїлі

19

Вам справді потрібен образ як 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()методів).

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

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

Дивіться також наступний підручник: Робота із зображеннями


16

У випадку, якщо хтось все ще шукає рішення без залежності, і якщо у вас є файл .

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);

Скажіть, що робити, якщо байтовий масив занадто великий, що може викликати OOM для купи? Чи є подібне рішення, яке використовуватиме JNI для зберігання байтів, і пізніше ми зможемо використовувати inputStream із збережених там даних (на зразок тимчасового кешу)?
андроїд розробник

14

Якщо ви не хочете використовувати бібліотеку 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;
}

Це трохи дивне рішення, довжина - це верхня межа довжини масиву. Якщо ви знаєте довжину, все, що вам потрібно, це: байт [] вихід = новий байт [довжина]; is.read (вихід); (але дивіться мою відповідь)
Люк Хатчісон

@ luke-hutchison, як я вже сказав, це рішення sun.misc.IOUtils. У найпоширеніших випадках ви не знаєте розмір InputStream наперед, тому якщо (length == -1) length = Integer.MAX_VALUE; застосовується. Це рішення працює, навіть якщо дана довжина більша за довжину InputStream.
Крістіан Краліч

@LukeHutchison Якщо ви знаєте довжину, ви можете обробити її кількома рядками. Якщо ви подивитеся на кожну відповідь, всі скаржаться, що довжина невідома. Нарешті, відповідь, яка є стандартною, може використовуватися з Java 7 Android, і не вимагає жодної зовнішньої бібліотеки.
Csaba Toth

11
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
Залежить від того, щоб знати розмір наперед.
stolsvik

2
Звичайно, але вони повинні знати розмір: "Я хочу прочитати зображення"
pihentagy

1
якщо ви знаєте розмір, тоді java надає код для вас. дивіться мою відповідь або google для "DataInputStream", і це метод readFully.
дерморіц

Ви повинні додати, is.close()якщо offset < bytes.lengthабо InputStreamзаповіт не буде закрито, якщо цей виняток буде кинуто.
Джаред Раммлер

3
Тоді краще, ви повинні використовувати спробу-ресурси
pihentagy

8
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();

Однак зазвичай ОС вже буфери достатньо, щоб це не викликало великої турботи для менших файлів. Це не так, як головка жорсткого диска буде читати кожен байт окремо (жорсткий диск - це поворотна скляна пластинка з магнітно-кодованою інформацією, трохи схожа на той дивний значок, який ми використовуємо для збереження даних: P).
Maarten Bodewes

6
@Maarten Bodewes: більшість пристроїв мають певний тип передачі блоку, тому не кожне зчитування () дійсно призведе до фактичного доступу до пристрою, але наявність виклику ОС на байт вже достатньо, щоб знищити продуктивність. У той час як обгортання InputStreamв BufferedInputStreamдо цього коду дозволить скоротити ОСА-дзвінки і значно зменшити недоліки продуктивності, що код буде робити непотрібну ручну роботу копіювання з одного буфера в інший.
Холгер

4

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

InputStream in = ...;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
in.transferTo( bos );
byte[] bytes = bos.toByteArray();

4
Яка різниця між цим і InputStram.readAllBytes()цим однолінійним?
Слава Семушин

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();
}

4
Слід використовувати пробні ресурси.
Віктор Стафуса

На прибирання в кінці потрібно робити остаточний блок у випадку помилок, інакше це може спричинити витік пам'яті.
MGDavies

2

Java 8 шлях (завдяки 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') і може бути невідповідним.


4
Це для String. ОП просить byte[].
FrozenFire

Це не тільки \rможе бути проблемою. Цей метод перетворює байти в символи і знову (використовуючи за замовчуванням набір символів для InputStreamReader). Будь-які байти, недійсні в кодуванні символів за замовчуванням (скажімо, -1 для UTF-8 в Linux), будуть пошкоджені, що може навіть змінити кількість байтів.
seanf

Здається, це гарна відповідь, але орієнтована на текст. Покупця остерігайтесь.
Wheezil

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 не потрібно закривати. Спробуйте / нарешті побудуйте, пропущені для читання


1

Дивіться InputStream.available()документацію:

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


1

Загорніть його в DataInputStream, якщо це з якоїсь причини поза столом, просто використовуйте читання, щоб забити його, поки він не дасть вам -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;
}

1

Ми спостерігаємо деяку затримку для декількох транзакцій AWS, перетворюючи S3-об'єкт у ByteArray.

Примітка: S3 Object - це документ PDF (максимальний розмір - 3 mb).

Ми використовуємо опцію №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

Інший випадок отримати правильний байтовий масив через потік, після відправки запиту на сервер і очікування відповіді.

/**
         * 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

Ви робите додаткову копію, якщо використовуєте ByteArrayOutputStream. Якщо ви знаєте довжину потоку перед тим, як почати його читати (наприклад, InputStream насправді є FileInputStream, і ви можете викликати file.length () у файлі, або InputStream є вхідним файлом InputStream в zipfile, і ви можете викликати 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);

Зверніть увагу: останній рядок вище стосується файлів, які врізаються під час зчитування потоку, якщо вам потрібно обробляти цю можливість, але якщо файл стає довше під час читання потоку, вміст у байтовому [] масиві не подовжується щоб включити новий вміст файлу, масив буде просто врізаний у стару довжину inputStreamLength .


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
Додайте пояснення з відповіддю, як ця відповідь допоможе ОП у вирішенні поточного питання
ρяσяρєя K

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

Java 7 та новіших версій:

import sun.misc.IOUtils;
...
InputStream in = ...;
byte[] buf = IOUtils.readFully(in, -1, false);

20
sun.misc.IOUtilsне є "Java 7". Це власний клас, специфічний для впровадження, який може не бути присутнім в інших реалізаціях JRE і може зникнути без будь-якого попередження в одному з наступних випусків.
Холгер


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;
   }
}

0

Рішення в Котліні (звичайно буде працювати і на 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://stackoverflow.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
    }

-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();
}

Закриття та промивання бос - це марно натискання клавіш. Закриття вхідного потоку скоріше допоможе. Читання одного байта за раз неефективно. Дивіться відповідь нумана.
акостадінов
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.