Bagaimana Anda membaca inputstream yang sama dua kali? Apakah mungkin untuk menyalinnya?
Saya perlu mendapatkan gambar dari web, menyimpannya secara lokal dan kemudian mengembalikan gambar yang disimpan. Saya hanya berpikir akan lebih cepat menggunakan aliran yang sama daripada memulai aliran baru ke konten yang diunduh dan kemudian membacanya lagi.
java
inputstream
Warpzit
sumber
sumber
Jawaban:
Anda dapat menggunakan
org.apache.commons.io.IOUtils.copy
untuk menyalin konten InputStream ke array byte, dan kemudian berulang kali membaca dari array byte menggunakan ByteArrayInputStream. Misalnya:sumber
Bergantung dari mana InputStream berasal, Anda mungkin tidak dapat menyetel ulang. Anda dapat memeriksa apakah
mark()
danreset()
didukung menggunakanmarkSupported()
.Jika ya, Anda dapat memanggil
reset()
InputStream untuk kembali ke awal. Jika tidak, Anda perlu membaca InputStream dari sumbernya lagi.sumber
InputStream
subsclasses likeBufferedInputStream
does support 'mark'jika
InputStream
dukungan Anda menggunakan mark, maka Anda dapatmark()
inputStream dan kemudianreset()
. jika AndaInputStrem
tidak mendukung tanda maka Anda dapat menggunakan kelasjava.io.BufferedInputStream
, sehingga Anda dapat menyematkan aliran Anda di dalamBufferedInputStream
seperti inisumber
BufferedInputStream.fill()
, ada bagian "buffer tumbuh", di mana ukuran buffer baru dibandingkan hanya denganmarklimit
danMAX_BUFFER_SIZE
.Anda dapat menggabungkan aliran input dengan PushbackInputStream. PushbackInputStream memungkinkan untuk unread (" write back ") byte yang sudah dibaca, jadi Anda bisa melakukan seperti ini:
Harap dicatat bahwa PushbackInputStream menyimpan buffer internal byte sehingga benar-benar membuat buffer di memori yang menyimpan byte "ditulis kembali".
Mengetahui pendekatan ini kita bisa melangkah lebih jauh dan menggabungkannya dengan FilterInputStream. FilterInputStream menyimpan aliran input asli sebagai delegasi. Hal ini memungkinkan untuk membuat definisi kelas baru yang memungkinkan data asli " belum dibaca " secara otomatis. Definisi kelas ini adalah sebagai berikut:
Kelas ini memiliki dua metode. Satu untuk membaca buffer yang ada (definisi analog dengan panggilan
public int read(byte b[], int off, int len)
kelas InputStream). Kedua yang mengembalikan buffer baru (ini mungkin lebih efektif jika ukuran buffer untuk dibaca tidak diketahui).Sekarang mari kita lihat kelas kita beraksi:
sumber
Jika Anda menggunakan implementasi
InputStream
, Anda dapat memeriksa hasilInputStream#markSupported()
yang memberi tahu Anda apakah Anda dapat menggunakan metodemark()
/reset()
.Jika Anda dapat menandai aliran saat Anda membaca, maka panggil
reset()
untuk kembali untuk memulai.Jika tidak bisa, Anda harus membuka aliran lagi.
Solusi lain adalah mengonversi InputStream ke array byte, lalu mengulanginya selama yang Anda butuhkan. Anda dapat menemukan beberapa solusi dalam posting ini Konversikan InputStream ke array byte di Java menggunakan libs pihak ketiga atau tidak. Perhatian, jika konten yang dibaca terlalu besar Anda mungkin mengalami beberapa masalah memori.
Terakhir, jika kebutuhan Anda adalah membaca gambar, gunakan:
Menggunakan
ImageIO#read(java.net.URL)
juga memungkinkan Anda menggunakan cache.sumber
ImageIO#read(java.net.URL)
: beberapa server web dan CDN mungkin menolak panggilan kosong (yaitu tanpa Agen Pengguna yang membuat server percaya bahwa panggilan tersebut berasal dari browser web) yang dibuat olehImageIO#read
. Dalam hal ini, menggunakanURLConnection.openConnection()
pengaturan agen pengguna ke koneksi itu + menggunakan `ImageIO.read (InputStream) akan, sebagian besar, melakukan trik.InputStream
bukan antarmukaBagaimana tentang:
sumber
Untuk memisahkan menjadi
InputStream
dua, sambil menghindari memuat semua data dalam memori , lalu memprosesnya secara mandiri:OutputStream
, tepatnya:PipedOutputStream
PipedInputStream
adalah hasilInputStream
.OutputStream
. Jadi, semua yang dibaca dari sourcingInputStream
, akan ditulis di keduanyaOutputStream
. Tidak perlu mengimplementasikannya, karena sudah dilakukan diTeeInputStream
(commons.io).Dalam utas terpisah, baca seluruh sumber inputStream, dan secara implisit data input ditransfer ke inputStreams target.
Berhati-hatilah untuk menutup inputStreams setelah dikonsumsi, dan tutup utas yang menjalankan:
TeeInputStream.readAllBytes()
Dalam kasus ini, Anda perlu membaginya menjadi beberapa
InputStream
, bukan hanya dua. Ganti dalam fragmen kode sebelumnya kelasTeeOutputStream
untuk implementasi Anda sendiri, yang akan merangkumList<OutputStream>
dan menggantiOutputStream
antarmuka:sumber
Ubah inputstream menjadi byte lalu teruskan ke fungsi savefile tempat Anda merakitnya menjadi inputstream. Juga dalam fungsi aslinya gunakan byte untuk digunakan untuk tugas lain
sumber
Jika ada yang menjalankan aplikasi Spring Boot, dan Anda ingin membaca isi respons a
RestTemplate
(itulah mengapa saya ingin membaca streaming dua kali), ada cara yang bersih (er) untuk melakukan ini.Pertama-tama, Anda perlu menggunakan Spring
StreamUtils
untuk menyalin aliran ke String:Tapi itu belum semuanya. Anda juga perlu menggunakan pabrik permintaan yang dapat menyangga streaming untuk Anda, seperti:
Atau, jika Anda menggunakan factory bean, maka (ini adalah Kotlin tapi bagaimanapun):
Sumber: https://objectpartners.com/2018/03/01/log-your-resttemplate-request-and-response-without-destroying-the-body/
sumber
Jika Anda menggunakan RestTemplate untuk melakukan panggilan http Cukup tambahkan pencegat. Badan respons di-cache dengan implementasi ClientHttpResponse. Sekarang inputstream dapat diambil dari respose sebanyak yang kita butuhkan
sumber