Визначте розмір InputStream


78

Наразі моя ситуація така: я повинен прочитати файл і помістити вміст InputStream. Згодом мені потрібно помістити вміст InputStreamмасиву в байтовий масив, який вимагає (наскільки мені відомо) розмір InputStream. Будь-які ідеї?

За запитом я покажу вхідний потік, який я створюю із завантаженого файлу

InputStream uploadedStream = null;
FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
java.util.List items = upload.parseRequest(request);      
java.util.Iterator iter = items.iterator();

while (iter.hasNext()) {
    FileItem item = (FileItem) iter.next();
    if (!item.isFormField()) {
        uploadedStream = item.getInputStream();
        //CHANGE uploadedStreambyte = item.get()
    }
}

Запит - це HttpServletRequestоб’єкт, подібний до FileItemFactoryі ServletFileUploadз пакета Apache Commons FileUpload.

Відповіді:


14

Я просто хотів додати, що Apache Commons IO має утиліти підтримки потоку для копіювання. (До речі, що ви маєте на увазі, розмістивши файл у вхідному потоці? Чи можете ви показати нам свій код?)

Редагувати:

Гаразд, що ви хочете зробити із вмістом елемента? Є такий, item.get() який повертає всю річ у байтовому масиві.

Редагувати2

item.getSize()поверне розмір завантаженого файлу .


В даний час я зберігаю файл у полі BLOB-бази даних і відправляю його як бінарний потік (вхід як вхідний потік), тепер мені потрібен вхідний потік у байтовому масиві, оскільки мені потрібно зробити підпис даних, а функція займає лише байт масиви.
ChronoXIII

1
item.get () створює масив байтів, як я вже згадував. І не турбуйтеся про продуктивність розміру, якщо ви не працюєте з кількома МБ зображеннями.
akarnokd

Це може бути так, оскільки зображення завантажується користувачем, і я, здається, не можу знайти спосіб автоматичного обрізання зображень на стороні сервера. :(
ChronoXIII

Що ви маєте на увазі під автоматичним обрізанням зображень? get () передасть вам весь завантажений файл (зображення) у байті []. Потім ви продовжуєте використовувати його на будь-якому OutputStream.write (), знову обертаєте його до ByteArrayInputStream тощо
akarnokd

1
Я змінив рішення для цього повідомлення, оскільки його відповідь була більш точно налаштована для моєї поточної установки
ChronoXIII

61

Це ДІЙСНО стара тема, але все одно це перше, що з’явилося, коли я погуглив проблему. Тому я просто хотів додати це:

InputStream inputStream = conn.getInputStream();
int length = inputStream.available();

Працював у мене. І НАБАГАТО простіші за інші відповіді тут.

Попередження Це рішення не забезпечує надійних результатів щодо загального розміру потоку. Крім JavaDoc:

Зауважте, що хоча деякі реалізації {@code InputStream} повертатимуть * загальну кількість байтів у потоці, багато хто не буде.


81
Я не думаю, що це точно. З Javadocs: "Зверніть увагу, що, хоча деякі реалізації InputStream повернуть загальну кількість байтів у потоці, багато хто не буде. Ніколи не є правильним використовувати значення повернення цього методу для виділення буфера, призначеного для зберігання всіх даних у цьому потік ". Отже, це могло спрацювати на вашій віртуальній машині, але може не працювати на чужій. docs.oracle.com/javase/7/docs/api/java/io/…
2013 р.,

3
О псих. Хороший улов! Я думаю, цей трюк не підходить для коду, який повинен бути портативним. Я використовував для шкільного проекту, тому він працював у мене. Хоча дякую, добре знати на майбутнє!
WB Reed

4
Це ідеально, коли у вас є всі дані в пам'яті, як у ByteArrayInputStream.
штабелер

9
Це НЕПРАВИЛЬНО! Як зазначено в документації щодо available()методу, "зауважте, що хоча деякі реалізації InputStream повертають загальну кількість байтів у потоці, багато хто не буде. Ніколи не правильно використовувати значення повернення цього методу для виділення буфера, призначеного для зберігання всіх дані в цьому потоці. "
GVillani82

4
Відповідь оманлива. Що стосується документів Android , там чітко сказано: "Зверніть увагу, що цей метод забезпечує настільки слабку гарантію, що він не надто корисний на практиці ".
Ксенія

38

Я прочитав би ByteArrayOutputStream, а потім зателефонував toByteArray (), щоб отримати результуючий байтовий масив. Вам не потрібно заздалегідь визначати розмір (хоча це можливо оптимізація, якщо ви це знаєте . У багатьох випадках ви цього не робите)


Здається, я виявив, що inputstream має метод toString (), і тоді я можу просто викликати getBytes (), щоб створити байтовий масив поза рядком. Мені цікаво, чи є якісь проблеми з продуктивністю від цього?
ChronoXIII

Потрібно бути обережним при перетворенні байтів / символів. Це спочатку байтовий потік? Якщо він містить символи, як вони кодуються і т. Д. Якщо ви отримуєте їх через мережеве підключення, я підозрюю, що це ваше головне вузьке місце в роботі, і я б не турбувався про накладні витрати на перетворення
Брайан Егню

В даний час я читаю вхідний потік із зображенням у базу даних як двійковий потік, це, здається, працює добре, оскільки я можу прочитати файл із зворотними словами та його все ще зображенням
ChronoXIII

2
Так. Ви отримуєте повний масив необроблених байтів, без перетворення, без проблем.
akarnokd

1
Хіба це насправді неефективно, якщо все, що вам потрібно - це розмір?
cdmckay

20

Ви не можете визначити обсяг даних у потоці, не прочитавши їх; однак ви можете попросити розмір файлу:

http://java.sun.com/javase/6/docs/api/java/io/File.html#length ()

Якщо це неможливо, ви можете записати прочитані байти з вхідного потоку до ByteArrayOutputStream, який буде зростати за необхідності.


У моєму сценарії вхідний потік містить завантажений файл із форми HTML, я не можу отримати розмір файлу, оскільки я не завантажую файл із жорсткого диска.
ChronoXIII

1
Якщо ваш вхідний потік має підтримку mark (), ви можете позначити його на початку і просто повністю прочитати - а потім скинути () і розпочати його обробку.
akarnokd

1
файл є зображенням (потенційно великим), тому читання потоку двічі спричинить проблеми з продуктивністю, чи не так?
ChronoXIII

Спробуй це. Якщо це дуже повільно, у вас є проблеми з продуктивністю. Інакше ні. Це так просто.
Бомбе

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

8

Для InputStream

org.apache.commons.io.IoUtils.toByteArray(inputStream).length()

Необов’язково <MultipartFile>

Stream.of(multipartFile.get()).mapToLong(file->file.getSize()).findFirst().getAsLong()

1
Я ніде не можу знайти IoUtils. Думаю, ви маєте на увазі org.apache.commons.io.IOUtils,
Остмонд

IOUtils.toByteArray (inputStream) .length у мене не працював.
dev4life

Так. Це не працює з IOUtils. Не знаю, чому цей метод і як це може бути корисним.
UM1979,

Це повернутий байтовий масив, який має метод length (). Я просто забув додати
парантез

4

Ви можете отримати розмір InputStream, використовуючи getBytes (inputStream) з Utils.java, перевірте це посилання

Отримати байти з Inputstream


Чи це також не повинно бути правильною відповіддю? Були проблеми з цим рішенням?
Арлін

2
Якщо ви планували використовувати свій вхідний потік, він буде вже прочитаний.
Ембер

1

Якщо явно ByteArrayInputStreamмаєте справу з тодішніми коментарями на цій сторінці, ви можете скористатися цією .available()функцією, щоб отримати розмір. Просто потрібно це зробити, перш ніж почати читати з нього.

З JavaDocs:

Повертає кількість решти байтів, які можна прочитати (або пропустити) з цього вхідного потоку. Повертається значення count - pos, що є кількістю байтів, що залишилося прочитати з вхідного буфера.

https://docs.oracle.com/javase/7/docs/api/java/io/ByteArrayInputStream.html#available ()


0

Якщо ви знаєте, що ваш InputStreama FileInputStreamабо a ByteArrayInputStream, ви можете використати невелике відображення, щоб отримати розмір потоку, не читаючи весь вміст . Ось приклад методу:

static long getInputLength(InputStream inputStream) {
    try {
        if (inputStream instanceof FilterInputStream) {
            FilterInputStream filtered = (FilterInputStream)inputStream;
            Field field = FilterInputStream.class.getDeclaredField("in");
            field.setAccessible(true);
            InputStream internal = (InputStream) field.get(filtered);
            return getInputLength(internal);
        } else if (inputStream instanceof ByteArrayInputStream) {
            ByteArrayInputStream wrapper = (ByteArrayInputStream)inputStream;
            Field field = ByteArrayInputStream.class.getDeclaredField("buf");
            field.setAccessible(true);
            byte[] buffer = (byte[])field.get(wrapper);
            return buffer.length;
        } else if (inputStream instanceof FileInputStream) {
            FileInputStream fileStream = (FileInputStream)inputStream;
            return fileStream.getChannel().size();
        }
    } catch (NoSuchFieldException | IllegalAccessException | IOException exception) {
        // Ignore all errors and just return -1.
    }
    return -1;
}

Я впевнений, це може бути розширено для підтримки додаткових вхідних потоків.


Якщо ви можете дозволити собі повторне створення inputStream і чекати його, ви можете прочитати все це, щоб отримати його розмір як запасний варіант для всіх них
розробник Android

-1

Використовуйте цей метод, вам просто потрібно пройти InputStream

public String readIt(InputStream is) {
    if (is != null) {
        BufferedReader reader = new BufferedReader(new InputStreamReader(is, "utf-8"), 8);

        StringBuilder sb = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            sb.append(line).append("\n");
        }
        is.close();
        return sb.toString();
    }
    return "error: ";
}

1
-1, це передбачає, що InputStreamце завжди рядок (не потрібно, оскільки це можуть бути двійкові дані), і в будь-якому випадку це не повертає розмір потоку, про який задавали питання. Крім того, він додає додаткові байти ( \n) до даних, тому sb.length()також буде неточним.
Vicky Chijwani

Добре, я розумію, тому це корисно, коли ви знаєте, що очікуєте отримати Stringпотік. \nнасправді потрібен, тому що вам потрібно надати певний формат отриманому вводу, я не хочу довгий чорізо String, тому навіть це \nвраховує.
Карлос. V

-1
    try {
        InputStream connInputStream = connection.getInputStream();
    } catch (IOException e) {
        e.printStackTrace();
    }

    int size = connInputStream.available();

int available () Повертає оцінку кількості байтів, які можна прочитати (або пропустити) з цього вхідного потоку без блокування наступним викликом методу для цього вхідного потоку. Наступним викликом може бути той самий потік або інший потік. Одноразове читання або пропуск з такої кількості байтів не заблокує, але може прочитати або пропустити меншу кількість байтів.

InputStream - Android SDK | Розробники Android


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