У меня есть InputStream, который я передаю методу для некоторой обработки. Я буду использовать тот же InputStream в другом методе, но после первой обработки InputStream оказывается закрытым внутри метода.
Как я могу клонировать InputStream для отправки в метод, который его закрывает? Есть другое решение?
РЕДАКТИРОВАТЬ: методы, которые закрывают InputStream является внешним методом из библиотеки. Я не могу контролировать закрытие или нет.
private String getContent(HttpURLConnection con) {
InputStream content = null;
String charset = "";
try {
content = con.getInputStream();
CloseShieldInputStream csContent = new CloseShieldInputStream(content);
charset = getCharset(csContent);
return IOUtils.toString(content,charset);
} catch (Exception e) {
System.out.println("Error downloading page: " + e);
return null;
}
}
private String getCharset(InputStream content) {
try {
Source parser = new Source(content);
return parser.getEncoding();
} catch (Exception e) {
System.out.println("Error determining charset: " + e);
return "UTF-8";
}
}
java
clone
inputstream
Ренато Динхани
источник
источник
Ответы:
Если все, что вы хотите сделать, это прочитать одну и ту же информацию более одного раза, а входные данные достаточно малы, чтобы поместиться в память, вы можете скопировать данные из вашего объекта
InputStream
в ByteArrayOutputStream .Тогда вы можете получить ассоциативный массив байт и открыт как многие «клонированный» ByteArrayInputStream s , как вам нравится.
Но если вам действительно нужно оставить исходный поток открытым для получения новых данных, вам нужно будет отследить этот внешний
close()
метод и не допустить его вызова каким-либо образом.ОБНОВЛЕНИЕ (2019):
Начиная с Java 9, средние биты можно заменить на
InputStream.transferTo
:источник
TeeInputStream
как описано в ответе здесь .Вы хотите использовать Apache
CloseShieldInputStream
:Это оболочка, которая предотвращает закрытие потока. Вы бы сделали что-то вроде этого.
источник
CloseShield
не работает, потому что ваш исходныйHttpURLConnection
входной поток где-то закрыт. Разве ваш метод не должен вызывать IOUtils с защищенным потокомIOUtils.toString(csContent,charset)
?close()
вызове, а в том, что Stream читается до конца. Посколькуmark()
и,reset()
возможно, не самые лучшие методы для http-соединений, возможно, вам стоит взглянуть на подход байтового массива, описанный в моем ответе.Вы не можете клонировать его, и то, как вы собираетесь решить свою проблему, зависит от источника данных.
Одним из решений является чтение всех данных из InputStream в байтовый массив, а затем создание ByteArrayInputStream вокруг этого байтового массива и передача этого входного потока в ваш метод.
Редактировать 1: То есть, если другой метод также должен прочитать те же данные. Т.е. вы хотите «сбросить» поток.
источник
Если данные, считываемые из потока, большие, я бы рекомендовал использовать TeeInputStream из Apache Commons IO. Таким образом, вы можете по существу реплицировать ввод и передать в качестве своего клона канал.
источник
Это может работать не во всех ситуациях, но вот что я сделал: я расширил класс FilterInputStream и выполняю необходимую обработку байтов, когда внешняя библиотека читает данные.
Затем вы просто передаете экземпляр того,
StreamBytesWithExtraProcessingInputStream
где вы бы прошли во входном потоке. С исходным входным потоком в качестве параметра конструктора.Следует отметить, что это работает байт за байтом, поэтому не используйте его, если требуется высокая производительность.
источник
UPD. Проверьте комментарий раньше. Это не совсем то, что спросили.
Если вы используете,
apache.commons
вы можете копировать потоки с помощьюIOUtils
.Вы можете использовать следующий код:
Вот полный пример, подходящий для вашей ситуации:
Этот код требует некоторых зависимостей:
MAVEN
Gradle
Вот ссылка DOC для этого метода:
Вы можете найти больше об этом
IOUtils
здесь: http://commons.apache.org/proper/commons-io/javadocs/api-2.4/org/apache/commons/io/IOUtils.html#toBufferedInputStream(java.io.InputStream)источник
Ниже решение с Kotlin.
Вы можете скопировать свой InputStream в ByteArray
Если вам нужно прочитать
byteInputStream
несколько раз, позвоните,byteInputStream.reset()
прежде чем читать снова.https://code.luasoftware.com/tutorials/kotlin/how-to-clone-inputstream/
источник
Класс ниже должен сделать свое дело. Просто создайте экземпляр, вызовите метод «multiply» и предоставьте исходный поток ввода и необходимое количество дубликатов.
Важно: вы должны использовать все клонированные потоки одновременно в отдельных потоках.
источник
Клонирование входного потока не может быть хорошей идеей, потому что это требует глубоких знаний о деталях клонируемого входного потока. Обходным путем для этого является создание нового входного потока, который снова читает из того же источника.
Таким образом, при использовании некоторых функций Java 8 это будет выглядеть так:
Этот метод имеет положительный эффект от повторного использования уже существующего кода - создание входного потока, инкапсулированного в
inputStreamSupplier
. И нет необходимости поддерживать второй путь кода для клонирования потока.С другой стороны, если чтение из потока является дорогостоящим (поскольку оно выполняется по соединению с низкой пропускной способностью), тогда этот метод удвоит затраты. Это можно обойти, используя конкретного поставщика, который сначала будет хранить потоковое содержимое локально и предоставит
InputStream
для этого теперь локальный ресурс.источник
is
?java.io.File
или,java.net.URL
например, для создания нового входного потока при каждом его вызове.