Saya memiliki InputStream yang saya berikan ke metode untuk melakukan pemrosesan. Saya akan menggunakan InputStream yang sama dalam metode lain, tetapi setelah pemrosesan pertama, InputStream tampaknya ditutup di dalam metode.
Bagaimana saya bisa mengkloning InputStream untuk mengirim ke metode yang menutupnya? Ada solusi lain?
EDIT: metode yang menutup InputStream adalah metode eksternal dari lib. Saya tidak punya kendali tentang penutupan atau tidak.
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
Renato Dinhani
sumber
sumber
Jawaban:
Jika semua yang ingin Anda lakukan adalah membaca informasi yang sama lebih dari satu kali, dan data input cukup kecil untuk masuk ke dalam memori, Anda dapat menyalin data dari Anda
InputStream
ke ByteArrayOutputStream .Kemudian Anda dapat memperoleh array terkait byte dan membuka sebanyak "kloning" ByteArrayInputStream s yang Anda inginkan.
Tetapi jika Anda benar-benar harus menjaga aliran asli terbuka untuk menerima data baru, maka Anda perlu melacak
close()
metode eksternal ini dan mencegahnya dipanggil entah bagaimana.PEMBARUAN (2019):
Karena Java 9 bit tengah dapat diganti dengan
InputStream.transferTo
:sumber
TeeInputStream
seperti dijelaskan dalam jawaban di sini .Anda ingin menggunakan Apache
CloseShieldInputStream
:Ini adalah pembungkus yang akan mencegah aliran ditutup. Anda akan melakukan sesuatu seperti ini.
sumber
CloseShield
tidak berfungsi karenaHttpURLConnection
aliran input awal Anda ditutup di suatu tempat. Bukankah seharusnya metode Anda memanggil IOUtils dengan aliran yang dilindungiIOUtils.toString(csContent,charset)
?close()
panggilan sama sekali, tetapi kenyataannya Stream sedang dibaca sampai akhir. Karenamark()
danreset()
mungkin bukan metode terbaik untuk koneksi http, mungkin Anda harus melihat pendekatan byte array yang dijelaskan pada jawaban saya.Anda tidak dapat mengkloningnya, dan bagaimana Anda akan memecahkan masalah Anda tergantung pada apa sumber data tersebut.
Salah satu solusinya adalah membaca semua data dari InputStream ke dalam array byte, dan kemudian membuat ByteArrayInputStream di sekitar array byte itu, dan meneruskan aliran input itu ke metode Anda.
Sunting 1: Yaitu, jika metode lain juga perlu membaca data yang sama. Yaitu Anda ingin "mengatur ulang" aliran.
sumber
Jika data yang dibaca dari aliran besar, saya akan merekomendasikan menggunakan TeeInputStream dari Apache Commons IO. Dengan begitu Anda pada dasarnya dapat mereplikasi input dan mengirimkan pipa sebagai klon Anda.
sumber
Ini mungkin tidak berfungsi dalam semua situasi, tetapi inilah yang saya lakukan: Saya memperluas kelas FilterInputStream dan melakukan pemrosesan byte yang diperlukan karena lib eksternal membaca data.
Kemudian Anda hanya melewati contoh di
StreamBytesWithExtraProcessingInputStream
mana Anda akan lulus dalam aliran input. Dengan input stream asli sebagai parameter konstruktor.Perlu dicatat bahwa ini berfungsi byte untuk byte, jadi jangan gunakan ini jika kinerja tinggi adalah persyaratan.
sumber
UPD. Periksa komentar sebelumnya. Bukan apa yang diminta.
Jika Anda menggunakan,
apache.commons
Anda dapat menyalin aliran menggunakanIOUtils
.Anda dapat menggunakan kode berikut:
Berikut ini adalah contoh lengkap yang cocok untuk situasi Anda:
Kode ini memerlukan beberapa dependensi:
MAVEN
GRADLE
Berikut ini adalah referensi DOC untuk metode ini:
Anda dapat menemukan lebih banyak tentang di
IOUtils
sini: http://commons.apache.org/proper/commons-io/javadocs/api-2.4/org/apache/commons/io/IOUtils.html#toBufferedInputStream(java.io.InputStream)sumber
Di bawah ini solusinya dengan Kotlin.
Anda dapat menyalin InputStream Anda ke ByteArray
Jika Anda perlu membaca
byteInputStream
berulang kali, hubungibyteInputStream.reset()
sebelum membaca lagi.https://code.luasoftware.com/tutorials/kotlin/how-to-clone-inputstream/
sumber
Kelas di bawah ini harus melakukan trik. Buat saja instance, panggil metode "multiply", dan berikan aliran input sumber dan jumlah duplikat yang Anda butuhkan.
Penting: Anda harus mengonsumsi semua aliran hasil kloning secara bersamaan di utas terpisah.
sumber
Mengkloning aliran input mungkin bukan ide yang baik, karena ini membutuhkan pengetahuan mendalam tentang detail aliran input yang dikloning. Solusi untuk ini adalah membuat aliran input baru yang membaca dari sumber yang sama lagi.
Jadi menggunakan beberapa fitur Java 8 ini akan terlihat seperti ini:
Metode ini memiliki efek positif sehingga akan menggunakan kembali kode yang sudah ada - pembuatan input stream dienkapsulasi
inputStreamSupplier
. Dan tidak perlu mempertahankan jalur kode kedua untuk kloning aliran.Di sisi lain, jika membaca dari sungai itu mahal (karena itu dilakukan melalui koneksi bandwidth rendah), maka metode ini akan menggandakan biaya. Ini dapat dielakkan dengan menggunakan pemasok tertentu yang akan menyimpan konten stream secara lokal terlebih dahulu dan menyediakan
InputStream
sumber daya yang sekarang lokal.sumber
is
?java.io.File
ataujava.net.URL
misalnya untuk membuat aliran input baru setiap kali dipanggil.