วิธีการแปลง OutputStream เป็น InputStream?


337

ฉันอยู่ในขั้นตอนของการพัฒนาที่ฉันมีสองโมดูลและจากที่หนึ่งผมได้รับการส่งออกเป็นสองและหนึ่งซึ่งยอมรับเฉพาะOutputStream InputStreamคุณรู้วิธีการแปลงOutputStreamเป็นInputStream(ไม่ใช่ในทางกลับกันฉันหมายถึงอย่างนี้จริงๆ) ว่าฉันจะสามารถเชื่อมต่อทั้งสองส่วนได้หรือไม่?

ขอบคุณ



3
@ c0mrade ตัวเลือกต้องการ IOUtils.copy ในทิศทางอื่นเท่านั้น เมื่อมีคนเขียนลงใน OutputStream คนอื่นจะสามารถใช้ใน InputStream ได้ นี่คือสิ่งที่ PipedOutputStream / PipedInputStream ทำ น่าเสียดายที่ Piped ไม่สามารถสร้างจากสตรีมอื่นได้
MeBigFatGuy

PipedOutputStream / PipedInputStream เป็นวิธีแก้ปัญหาหรือไม่
Waypoint

โดยทั่วไปเพื่อให้ PipedStreams ทำงานในกรณีของคุณ OutputStream ของคุณจะต้องถูกสร้างขึ้นnew YourOutputStream(thePipedOutputStream)และnew YourInputStream(thePipedInputStream)สิ่งที่อาจไม่ใช่วิธีการทำงานของสตรีมของคุณ ดังนั้นฉันไม่คิดว่านี่เป็นวิธีแก้ปัญหา
MeBigFatGuy

คำตอบ:


109

An OutputStreamคือที่ที่คุณเขียนข้อมูลไป หากบางโมดูลตีแผ่OutputStreamความคาดหวังคือมีบางสิ่งที่อ่านที่ปลายอีกด้าน

สิ่งที่เสี่ยงInputStreamบนมืออื่น ๆ ที่แสดงให้เห็นว่าคุณจะต้องฟังกระแสนี้และจะมีข้อมูลที่คุณสามารถอ่าน

ดังนั้นจึงเป็นไปได้ที่จะเชื่อมต่อInputStreamกับOutputStream

InputStream----read---> intermediateBytes[n] ----write----> OutputStream

ในฐานะที่เป็นคน metioned นี่คือสิ่งที่copy()วิธีการจากIOUtilsช่วยให้คุณทำ มันไม่มีเหตุผลที่จะไปทางอื่น ... หวังว่ามันจะสมเหตุสมผล

UPDATE:

แน่นอนยิ่งฉันคิดในเรื่องนี้มากเท่าไหร่ฉันก็ยิ่งเห็นว่าสิ่งนี้เป็นสิ่งจำเป็นจริงๆ ฉันรู้ว่าบางความคิดเห็นที่กล่าวถึงPipedกระแสข้อมูลอินพุต / ouput แต่มีความเป็นไปได้อื่น

หากกระแสข้อมูลขาออกที่เปิดเผยคือ a ByteArrayOutputStreamคุณจะสามารถรับเนื้อหาทั้งหมดได้เสมอโดยการเรียกtoByteArray()วิธีการนั้น จากนั้นคุณสามารถสร้าง wrapper กระแสข้อมูลโดยใช้ByteArrayInputStreamคลาสย่อย สองตัวนี้เป็นหลอกลำธารโดยปกติแล้วพวกมันทั้งสองห่ออาร์เรย์จำนวนหนึ่งเท่านั้น ดังนั้นการใช้ลำธารด้วยวิธีนี้จึงเป็นไปได้ในทางเทคนิค แต่สำหรับฉันแล้วมันยังคงแปลกมาก ...


4
คัดลอก () ทำสิ่งนี้กับระบบปฏิบัติการตาม API ฉันต้องการให้ทำย้อนหลัง
Waypoint

1
ดูการแก้ไขของฉันที่ด้านบนมันเป็นสิ่งจำเป็นสำหรับฉันที่จะทำให้เกิดการเปลี่ยนแปลง
Waypoint

88
usecase นั้นง่ายมาก: ลองนึกภาพว่าคุณมีไลบรารี่ที่เป็นอนุกรม (เช่น serializing เป็น JSON) และ transport layer (เช่น Tomcat) ซึ่งใช้ InputStream ดังนั้นคุณต้องไพพ์ OutputStream จาก JSON ผ่านการเชื่อมต่อ HTTP ที่ต้องการอ่านจาก InputStream
JBCP

6
สิ่งนี้มีประโยชน์เมื่อทำการทดสอบหน่วยและคุณคล่องแคล่วอย่างมากเกี่ยวกับการหลีกเลี่ยงการสัมผัสระบบไฟล์
Jon

28
ความคิดเห็นของ @JBCP นั้นเปิดอยู่ อีกกรณีการใช้งานคือการใช้ PDFBox เพื่อสร้าง PDF ในระหว่างการร้องขอ HTTP PDFBox ใช้ OutputStream เพื่อบันทึกวัตถุ PDF และ REST API ยอมรับ InputStream เพื่อตอบกลับลูกค้า ดังนั้น OutputStream -> InputStream เป็นกรณีการใช้งานจริงมาก
John Manko

200

ดูเหมือนจะมีลิงค์และสิ่งอื่น ๆ มากมาย แต่ไม่มีรหัสจริงที่ใช้ไพพ์ ข้อดีของการใช้java.io.PipedInputStreamและjava.io.PipedOutputStreamคือไม่มีการใช้หน่วยความจำเพิ่มเติม ByteArrayOutputStream.toByteArray()ส่งคืนสำเนาของบัฟเฟอร์ดั้งเดิมดังนั้นนั่นหมายความว่าสิ่งที่คุณมีในหน่วยความจำตอนนี้คุณมีสำเนาสองชุด จากนั้นการเขียนไปยังInputStreamวิธีการที่คุณมีสามสำเนาของข้อมูล

รหัส:

// take the copy of the stream and re-write it to an InputStream
PipedInputStream in = new PipedInputStream();
final PipedOutputStream out = new PipedOutputStream(in);
new Thread(new Runnable() {
    public void run () {
        try {
            // write the original OutputStream to the PipedOutputStream
            // note that in order for the below method to work, you need
            // to ensure that the data has finished writing to the
            // ByteArrayOutputStream
            originalByteArrayOutputStream.writeTo(out);
        }
        catch (IOException e) {
            // logging and exception handling should go here
        }
        finally {
            // close the PipedOutputStream here because we're done writing data
            // once this thread has completed its run
            if (out != null) {
                // close the PipedOutputStream cleanly
                out.close();
            }
        }   
    }
}).start();

รหัสนี้จะถือว่าoriginalByteArrayOutputStreamเป็นByteArrayOutputStreamเพราะมันมักจะเป็นกระแสเอาท์พุทที่ใช้งานได้เท่านั้นเว้นแต่คุณจะเขียนไปยังไฟล์ ฉันหวังว่านี่จะช่วยได้! สิ่งที่ยอดเยี่ยมเกี่ยวกับเรื่องนี้ก็คือเนื่องจากมันอยู่ในเธรดแยกต่างหากมันยังทำงานแบบขนานดังนั้นสิ่งที่กำลังบริโภคอินพุตสตรีมของคุณจะถูกสตรีมมิ่งจากสตรีมเอาต์พุตเก่าของคุณด้วย สิ่งนี้มีประโยชน์เพราะบัฟเฟอร์อาจมีขนาดเล็กลงและคุณจะมีเวลาแฝงน้อยลงและใช้หน่วยความจำน้อยลง


21
ฉันโหวตสิ่งนี้แล้ว แต่จะดีกว่าถ้าส่งผ่านoutไปยังinconstructor ของมิฉะนั้นคุณอาจได้รับข้อยกเว้นท่อปิดinเนื่องจากสภาพการแข่งขัน (ซึ่งฉันมีประสบการณ์) ใช้ Java 8 Lambdas:PipedInputStream in = new PipedInputStream(out); ((Runnable)() -> {originalOutputStream.writeTo(out);}).run(); return in;
John Manko

1
@JohnManko อืม ... ฉันไม่เคยมีปัญหานั้น คุณเคยเจอปัญหานี้หรือไม่เพราะมีเธรดอื่นหรือเธรดหลักกำลังโทรออกมาปิด () เป็นความจริงที่รหัสนี้จะถือว่า PipedOutputStream ของคุณมีอายุการใช้งานยาวนานกว่าoriginalOutputStreamที่ควรจะเป็นจริง แต่ไม่ได้สมมติว่าคุณควบคุมการสตรีมของคุณอย่างไร ที่เหลือขึ้นอยู่กับนักพัฒนา ไม่มีอะไรในรหัสนี้ที่จะทำให้เกิดข้อยกเว้นไปป์ไลน์ที่ปิดหรือแตก
mikeho

3
ไม่กรณีของฉันเกิดจากเมื่อฉันจัดเก็บ PDF ใน Mongo GridFS แล้วสตรีมไปยังไคลเอ็นต์โดยใช้ Jax-RS MongoDB ใช้ OutputStream แต่ Jax-RS ต้องการ InputStream เมธอดพา ธ ของฉันจะกลับไปที่คอนเทนเนอร์ด้วย InputStream ก่อนที่จะสร้าง OutputStream อย่างสมบูรณ์ดูเหมือนว่า (บางทีบัฟเฟอร์ยังไม่ได้ถูกแคช) อย่างไรก็ตาม Jax-RS จะปิดการวางท่อยกเว้นใน InputStream แปลก แต่นั่นคือสิ่งที่เกิดขึ้นครึ่งเวลา การเปลี่ยนเป็นโค้ดด้านบนป้องกันไม่ให้
John Manko

1
@JohnManko ฉันกำลังมองหาสิ่งนี้มากขึ้นและฉันเห็นจากPipedInputStreamJavadocs: มีการกล่าวถึงการขาดไปของเธรดถ้าเธรดที่ให้ไบต์ข้อมูลไปยังกระแสเอาต์พุต piped ที่เชื่อมต่อนั้นไม่มีชีวิตอีกต่อไป ดังนั้นสิ่งที่ฉันสงสัยคือถ้าคุณใช้ตัวอย่างด้านบนเธรดจะเสร็จสมบูรณ์ก่อนที่Jax-RSจะใช้กระแสข้อมูลอินพุต ในเวลาเดียวกันฉันมองไปที่MongoDB Javadocs GridFSDBFileมีกระแสอินพุตดังนั้นทำไมไม่เพียงแค่ผ่านที่Jax อาร์เอส ?
mikeho

3
@DennisCheung ใช่แน่นอน ไม่มีอะไรฟรี แต่มันจะมีขนาดเล็กกว่าสำเนา 15MB อย่างแน่นอน การปรับให้เหมาะสมจะรวมถึงการใช้เธรดพูลแทนการลด GC churn ด้วยการสร้างเธรด / วัตถุคงที่
mikeho

40

เนื่องจากอินพุตและเอาต์พุตสตรีมเป็นเพียงจุดเริ่มต้นและจุดสิ้นสุดโซลูชันคือการจัดเก็บข้อมูลชั่วคราวในอาร์เรย์ไบต์ ดังนั้นคุณต้องสร้างกลางByteArrayOutputStreamจากการที่คุณสร้างที่ใช้เป็นข้อมูลใหม่byte[]ByteArrayInputStream

public void doTwoThingsWithStream(InputStream inStream, OutputStream outStream){ 
  //create temporary bayte array output stream
  ByteArrayOutputStream baos = new ByteArrayOutputStream();
  doFirstThing(inStream, baos);
  //create input stream from baos
  InputStream isFromFirstData = new ByteArrayInputStream(baos.toByteArray()); 
  doSecondThing(isFromFirstData, outStream);
}

หวังว่ามันจะช่วย


baos.toByteArray () สร้างสำเนาด้วย System.arraycopy ขอบคุณ @mikeho สำหรับการชี้ให้เห็นdeveloper.classpath.org/doc/java/io/…
Mitja Gustin

20

คุณจะต้องมีระดับกลางซึ่งจะบัฟเฟอร์ระหว่าง แต่ละครั้งInputStream.read(byte[]...)จะถูกเรียกคลาสบัฟเฟอร์จะเติมอาร์เรย์ส่งผ่านเป็นไบต์พร้อมกับก้อนถัดไปที่ส่งจากOutputStream.write(byte[]...)จะเรียกว่าชั้นบัฟเฟอร์จะเติมผ่านในอาร์เรย์ไบต์กับก้อนต่อไปที่ผ่านมาจากเนื่องจากขนาดของชิ้นอาจไม่เหมือนกันคลาสอะแด็ปเตอร์จะต้องเก็บจำนวนหนึ่งไว้จนกว่าจะมีจำนวนเพียงพอที่จะเติมบัฟเฟอร์การอ่านและ / หรือสามารถเก็บบัฟเฟอร์ที่ล้นได้

บทความนี้มีรายละเอียดที่ดีของวิธีการที่แตกต่างกันไม่กี่ปัญหานี้:

http://blog.ostermiller.org/convert-java-outputstream-inputstream


1
ขอบคุณ @mckamey วิธีการที่ใช้บัฟเฟอร์แบบวงกลมเป็นสิ่งที่ฉันต้องการ!
Hui Wang

18
ByteArrayOutputStream buffer = (ByteArrayOutputStream) aOutputStream;
byte[] bytes = buffer.toByteArray();
InputStream inputStream = new ByteArrayInputStream(bytes);

2
คุณไม่ควรใช้สิ่งนี้เนื่องจากtoByteArray()เมธอด body เป็นเช่นนี้return Arrays.copyOf(buf, count);ซึ่งส่งคืนอาร์เรย์ใหม่
Root G

17

easystreamห้องสมุดเปิดแหล่งที่มาได้รับการสนับสนุนโดยตรงในการแปลง OutputStream ไปยัง InputStream: http://io-tools.sourceforge.net/easystream/tutorial/tutorial.html

พวกเขายังแสดงรายการตัวเลือกอื่น ๆ : http://io-tools.sourceforge.net/easystream/OutputStream_to_InputStream.html


1
ใช่! ใช้อีสเตอร์สตรีม!
smartwjw

9

ผมพบปัญหาเดียวกันกับการแปลงByteArrayOutputStreamไปByteArrayInputStreamและแก้ไขได้โดยใช้คลาสที่ได้รับจากการByteArrayOutputStreamที่สามารถกลับมาเป็นที่เริ่มต้นได้ด้วยบัฟเฟอร์ภายในของByteArrayInputStream ByteArrayOutputStreamวิธีนี้ไม่มีการใช้หน่วยความจำเพิ่มเติมและ 'การแปลง' รวดเร็วมาก:

package info.whitebyte.utils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

/**
 * This class extends the ByteArrayOutputStream by 
 * providing a method that returns a new ByteArrayInputStream
 * which uses the internal byte array buffer. This buffer
 * is not copied, so no additional memory is used. After
 * creating the ByteArrayInputStream the instance of the
 * ByteArrayInOutStream can not be used anymore.
 * <p>
 * The ByteArrayInputStream can be retrieved using <code>getInputStream()</code>.
 * @author Nick Russler
 */
public class ByteArrayInOutStream extends ByteArrayOutputStream {
    /**
     * Creates a new ByteArrayInOutStream. The buffer capacity is
     * initially 32 bytes, though its size increases if necessary.
     */
    public ByteArrayInOutStream() {
        super();
    }

    /**
     * Creates a new ByteArrayInOutStream, with a buffer capacity of
     * the specified size, in bytes.
     *
     * @param   size   the initial size.
     * @exception  IllegalArgumentException if size is negative.
     */
    public ByteArrayInOutStream(int size) {
        super(size);
    }

    /**
     * Creates a new ByteArrayInputStream that uses the internal byte array buffer 
     * of this ByteArrayInOutStream instance as its buffer array. The initial value 
     * of pos is set to zero and the initial value of count is the number of bytes 
     * that can be read from the byte array. The buffer array is not copied. This 
     * instance of ByteArrayInOutStream can not be used anymore after calling this
     * method.
     * @return the ByteArrayInputStream instance
     */
    public ByteArrayInputStream getInputStream() {
        // create new ByteArrayInputStream that respects the current count
        ByteArrayInputStream in = new ByteArrayInputStream(this.buf, 0, this.count);

        // set the buffer of the ByteArrayOutputStream 
        // to null so it can't be altered anymore
        this.buf = null;

        return in;
    }
}

ฉันใส่เนื้อหาใน github: https://github.com/nickrussler/ByteArrayInOutStream


เกิดอะไรขึ้นถ้าเนื้อหาไม่พอดีกับบัฟเฟอร์?
Vadimo

จากนั้นคุณไม่ควรใช้ ByteArrayInputStream ตั้งแต่แรก
Nick Russler

วิธีนี้จะมีหน่วยความจำทั้งหมดในไบต์ สำหรับไฟล์ขนาดเล็กสิ่งนี้จะใช้ได้ แต่คุณสามารถใช้ getBytes () บน ByteArrayOutput Stream ได้ด้วย
Vadimo

1
หากคุณหมายถึงtoByteArrayสิ่งนี้จะทำให้บัฟเฟอร์ภายในถูกคัดลอกซึ่งจะใช้หน่วยความจำสองเท่าของวิธีการของฉัน แก้ไข: เข้าใจแล้วสำหรับไฟล์ขนาดเล็กงานนี้แน่นอน ..
Nick Russler

เสียเวลา. ByteArrayOutputStream มีเมธอด writeTo เพื่อถ่ายโอนเนื้อหาไปยังเอาต์พุตสตรีมอื่น
Tony BenBrahim

3

ไลบรารีio-extrasอาจมีประโยชน์ ตัวอย่างเช่นหากคุณต้องการ gzip InputStreamใช้GZIPOutputStreamและคุณต้องการให้เกิดขึ้นพร้อมกัน (ใช้ขนาดบัฟเฟอร์เริ่มต้นของ 8192):

InputStream is = ...
InputStream gz = IOUtil.pipe(is, o -> new GZIPOutputStream(o));

โปรดทราบว่าห้องสมุดมีหน่วยทดสอบครอบคลุม 100% (สำหรับหลักสูตรที่คุ้มค่า!) และอยู่ใน Maven Central การพึ่งพา Maven คือ:

<dependency>
  <groupId>com.github.davidmoten</groupId>
  <artifactId>io-extras</artifactId>
  <version>0.1</version>
</dependency>

ให้แน่ใจว่าได้ตรวจสอบรุ่นที่ใหม่กว่า


0

จากมุมมองของฉัน java.io.PipedInputStream / java.io.PipedOutputStream เป็นตัวเลือกที่ดีที่สุดในการพิจารณา ในบางสถานการณ์คุณอาจต้องการใช้ ByteArrayInputStream / ByteArrayOutputStream ปัญหาคือคุณต้องทำซ้ำบัฟเฟอร์เพื่อแปลง ByteArrayOutputStream เป็น ByteArrayInputStream ByteArrayOutpuStream / ByteArrayInputStream จะถูก จำกัด ที่ 2GB นี่คือการใช้ OutpuStream / InputStream ฉันเขียนเพื่อบายพาสข้อ จำกัด ByteArrayOutputStream / ByteArrayInputStream ByteArrayInputStream (รหัส Scala แต่เข้าใจได้ง่ายสำหรับผู้พัฒนา Java java):

import java.io.{IOException, InputStream, OutputStream}

import scala.annotation.tailrec

/** Acts as a replacement for ByteArrayOutputStream
  *
  */
class HugeMemoryOutputStream(capacity: Long) extends OutputStream {
  private val PAGE_SIZE: Int = 1024000
  private val ALLOC_STEP: Int = 1024

  /** Pages array
    *
    */
  private var streamBuffers: Array[Array[Byte]] = Array.empty[Array[Byte]]

  /** Allocated pages count
    *
    */
  private var pageCount: Int = 0

  /** Allocated bytes count
    *
    */
  private var allocatedBytes: Long = 0

  /** Current position in stream
    *
    */
  private var position: Long = 0

  /** Stream length
    *
    */
  private var length: Long = 0

  allocSpaceIfNeeded(capacity)

  /** Gets page count based on given length
    *
    * @param length   Buffer length
    * @return         Page count to hold the specified amount of data
    */
  private def getPageCount(length: Long) = {
    var pageCount = (length / PAGE_SIZE).toInt + 1

    if ((length % PAGE_SIZE) == 0) {
      pageCount -= 1
    }

    pageCount
  }

  /** Extends pages array
    *
    */
  private def extendPages(): Unit = {
    if (streamBuffers.isEmpty) {
      streamBuffers = new Array[Array[Byte]](ALLOC_STEP)
    }
    else {
      val newStreamBuffers = new Array[Array[Byte]](streamBuffers.length + ALLOC_STEP)
      Array.copy(streamBuffers, 0, newStreamBuffers, 0, streamBuffers.length)
      streamBuffers = newStreamBuffers
    }

    pageCount = streamBuffers.length
  }

  /** Ensures buffers are bug enough to hold specified amount of data
    *
    * @param value  Amount of data
    */
  private def allocSpaceIfNeeded(value: Long): Unit = {
    @tailrec
    def allocSpaceIfNeededIter(value: Long): Unit = {
      val currentPageCount = getPageCount(allocatedBytes)
      val neededPageCount = getPageCount(value)

      if (currentPageCount < neededPageCount) {
        if (currentPageCount == pageCount) extendPages()

        streamBuffers(currentPageCount) = new Array[Byte](PAGE_SIZE)
        allocatedBytes = (currentPageCount + 1).toLong * PAGE_SIZE

        allocSpaceIfNeededIter(value)
      }
    }

    if (value < 0) throw new Error("AllocSpaceIfNeeded < 0")
    if (value > 0) {
      allocSpaceIfNeededIter(value)

      length = Math.max(value, length)
      if (position > length) position = length
    }
  }

  /**
    * Writes the specified byte to this output stream. The general
    * contract for <code>write</code> is that one byte is written
    * to the output stream. The byte to be written is the eight
    * low-order bits of the argument <code>b</code>. The 24
    * high-order bits of <code>b</code> are ignored.
    * <p>
    * Subclasses of <code>OutputStream</code> must provide an
    * implementation for this method.
    *
    * @param      b the <code>byte</code>.
    */
  @throws[IOException]
  override def write(b: Int): Unit = {
    val buffer: Array[Byte] = new Array[Byte](1)

    buffer(0) = b.toByte

    write(buffer)
  }

  /**
    * Writes <code>len</code> bytes from the specified byte array
    * starting at offset <code>off</code> to this output stream.
    * The general contract for <code>write(b, off, len)</code> is that
    * some of the bytes in the array <code>b</code> are written to the
    * output stream in order; element <code>b[off]</code> is the first
    * byte written and <code>b[off+len-1]</code> is the last byte written
    * by this operation.
    * <p>
    * The <code>write</code> method of <code>OutputStream</code> calls
    * the write method of one argument on each of the bytes to be
    * written out. Subclasses are encouraged to override this method and
    * provide a more efficient implementation.
    * <p>
    * If <code>b</code> is <code>null</code>, a
    * <code>NullPointerException</code> is thrown.
    * <p>
    * If <code>off</code> is negative, or <code>len</code> is negative, or
    * <code>off+len</code> is greater than the length of the array
    * <code>b</code>, then an <tt>IndexOutOfBoundsException</tt> is thrown.
    *
    * @param      b   the data.
    * @param      off the start offset in the data.
    * @param      len the number of bytes to write.
    */
  @throws[IOException]
  override def write(b: Array[Byte], off: Int, len: Int): Unit = {
    @tailrec
    def writeIter(b: Array[Byte], off: Int, len: Int): Unit = {
      val currentPage: Int = (position / PAGE_SIZE).toInt
      val currentOffset: Int = (position % PAGE_SIZE).toInt

      if (len != 0) {
        val currentLength: Int = Math.min(PAGE_SIZE - currentOffset, len)
        Array.copy(b, off, streamBuffers(currentPage), currentOffset, currentLength)

        position += currentLength

        writeIter(b, off + currentLength, len - currentLength)
      }
    }

    allocSpaceIfNeeded(position + len)
    writeIter(b, off, len)
  }

  /** Gets an InputStream that points to HugeMemoryOutputStream buffer
    *
    * @return InputStream
    */
  def asInputStream(): InputStream = {
    new HugeMemoryInputStream(streamBuffers, length)
  }

  private class HugeMemoryInputStream(streamBuffers: Array[Array[Byte]], val length: Long) extends InputStream {
    /** Current position in stream
      *
      */
    private var position: Long = 0

    /**
      * Reads the next byte of data from the input stream. The value byte is
      * returned as an <code>int</code> in the range <code>0</code> to
      * <code>255</code>. If no byte is available because the end of the stream
      * has been reached, the value <code>-1</code> is returned. This method
      * blocks until input data is available, the end of the stream is detected,
      * or an exception is thrown.
      *
      * <p> A subclass must provide an implementation of this method.
      *
      * @return the next byte of data, or <code>-1</code> if the end of the
      *         stream is reached.
      */
    @throws[IOException]
    def read: Int = {
      val buffer: Array[Byte] = new Array[Byte](1)

      if (read(buffer) == 0) throw new Error("End of stream")
      else buffer(0)
    }

    /**
      * Reads up to <code>len</code> bytes of data from the input stream into
      * an array of bytes.  An attempt is made to read as many as
      * <code>len</code> bytes, but a smaller number may be read.
      * The number of bytes actually read is returned as an integer.
      *
      * <p> This method blocks until input data is available, end of file is
      * detected, or an exception is thrown.
      *
      * <p> If <code>len</code> is zero, then no bytes are read and
      * <code>0</code> is returned; otherwise, there is an attempt to read at
      * least one byte. If no byte is available because the stream is at end of
      * file, the value <code>-1</code> is returned; otherwise, at least one
      * byte is read and stored into <code>b</code>.
      *
      * <p> The first byte read is stored into element <code>b[off]</code>, the
      * next one into <code>b[off+1]</code>, and so on. The number of bytes read
      * is, at most, equal to <code>len</code>. Let <i>k</i> be the number of
      * bytes actually read; these bytes will be stored in elements
      * <code>b[off]</code> through <code>b[off+</code><i>k</i><code>-1]</code>,
      * leaving elements <code>b[off+</code><i>k</i><code>]</code> through
      * <code>b[off+len-1]</code> unaffected.
      *
      * <p> In every case, elements <code>b[0]</code> through
      * <code>b[off]</code> and elements <code>b[off+len]</code> through
      * <code>b[b.length-1]</code> are unaffected.
      *
      * <p> The <code>read(b,</code> <code>off,</code> <code>len)</code> method
      * for class <code>InputStream</code> simply calls the method
      * <code>read()</code> repeatedly. If the first such call results in an
      * <code>IOException</code>, that exception is returned from the call to
      * the <code>read(b,</code> <code>off,</code> <code>len)</code> method.  If
      * any subsequent call to <code>read()</code> results in a
      * <code>IOException</code>, the exception is caught and treated as if it
      * were end of file; the bytes read up to that point are stored into
      * <code>b</code> and the number of bytes read before the exception
      * occurred is returned. The default implementation of this method blocks
      * until the requested amount of input data <code>len</code> has been read,
      * end of file is detected, or an exception is thrown. Subclasses are encouraged
      * to provide a more efficient implementation of this method.
      *
      * @param      b   the buffer into which the data is read.
      * @param      off the start offset in array <code>b</code>
      *                 at which the data is written.
      * @param      len the maximum number of bytes to read.
      * @return the total number of bytes read into the buffer, or
      *         <code>-1</code> if there is no more data because the end of
      *         the stream has been reached.
      * @see java.io.InputStream#read()
      */
    @throws[IOException]
    override def read(b: Array[Byte], off: Int, len: Int): Int = {
      @tailrec
      def readIter(acc: Int, b: Array[Byte], off: Int, len: Int): Int = {
        val currentPage: Int = (position / PAGE_SIZE).toInt
        val currentOffset: Int = (position % PAGE_SIZE).toInt

        val count: Int = Math.min(len, length - position).toInt

        if (count == 0 || position >= length) acc
        else {
          val currentLength = Math.min(PAGE_SIZE - currentOffset, count)
          Array.copy(streamBuffers(currentPage), currentOffset, b, off, currentLength)

          position += currentLength

          readIter(acc + currentLength, b, off + currentLength, len - currentLength)
        }
      }

      readIter(0, b, off, len)
    }

    /**
      * Skips over and discards <code>n</code> bytes of data from this input
      * stream. The <code>skip</code> method may, for a variety of reasons, end
      * up skipping over some smaller number of bytes, possibly <code>0</code>.
      * This may result from any of a number of conditions; reaching end of file
      * before <code>n</code> bytes have been skipped is only one possibility.
      * The actual number of bytes skipped is returned. If <code>n</code> is
      * negative, the <code>skip</code> method for class <code>InputStream</code> always
      * returns 0, and no bytes are skipped. Subclasses may handle the negative
      * value differently.
      *
      * The <code>skip</code> method of this class creates a
      * byte array and then repeatedly reads into it until <code>n</code> bytes
      * have been read or the end of the stream has been reached. Subclasses are
      * encouraged to provide a more efficient implementation of this method.
      * For instance, the implementation may depend on the ability to seek.
      *
      * @param      n the number of bytes to be skipped.
      * @return the actual number of bytes skipped.
      */
    @throws[IOException]
    override def skip(n: Long): Long = {
      if (n < 0) 0
      else {
        position = Math.min(position + n, length)
        length - position
      }
    }
  }
}

ใช้งานง่ายไม่มีการทำสำเนาบัฟเฟอร์ไม่ จำกัด หน่วยความจำ 2GB

val out: HugeMemoryOutputStream = new HugeMemoryOutputStream(initialCapacity /*may be 0*/)

out.write(...)
...

val in1: InputStream = out.asInputStream()

in1.read(...)
...

val in2: InputStream = out.asInputStream()

in2.read(...)
...

-1

หากคุณต้องการสร้าง OutputStream จาก InputStream มีปัญหาพื้นฐานหนึ่งข้อ วิธีการเขียนไปยังบล็อก OutputStream จนกว่าจะเสร็จสิ้น ดังนั้นผลลัพธ์จะพร้อมใช้งานเมื่อวิธีการเขียนเสร็จสิ้น สิ่งนี้มี 2 ผล:

  1. หากคุณใช้เพียงเธรดเดียวคุณต้องรอจนกว่าทุกอย่างจะถูกเขียน (ดังนั้นคุณต้องจัดเก็บข้อมูลของสตรีมในหน่วยความจำหรือดิสก์)
  2. ถ้าคุณต้องการเข้าถึงข้อมูลก่อนที่จะเสร็จสิ้นคุณต้องมีเธรดที่สอง

Variant 1 สามารถนำมาใช้โดยใช้อาร์เรย์ไบต์หรือยื่น Variant 1 สามารถนำไปใช้งานได้โดยใช้ pipies (ไม่ว่าจะโดยตรงหรือแบบ abstraction พิเศษเช่น RingBuffer หรือ google lib จากความคิดเห็นอื่น)

แน่นอนด้วย java มาตรฐานไม่มีวิธีอื่นในการแก้ปัญหา แต่ละโซลูชันเป็นการนำข้อมูลมาใช้อย่างใดอย่างหนึ่ง

มีแนวคิดหนึ่งที่เรียกว่า "ความต่อเนื่อง" (ดูวิกิพีเดียเพื่อดูรายละเอียด) ในกรณีนี้โดยทั่วไปหมายความว่า:

  • มีเอาต์พุตพิเศษที่คาดว่าจะมีข้อมูลจำนวนหนึ่ง
  • หากถึงจำนวนกระสุนลำธารจะให้การควบคุมกับมันซึ่งเป็นกระแสข้อมูลพิเศษ
  • สตรีมอินพุตทำให้จำนวนข้อมูลพร้อมใช้งานจนกว่าจะถูกอ่านหลังจากนั้นจะส่งผ่านการควบคุมกลับไปยังเอาต์พุตสตรีม

ในขณะที่บางภาษามีแนวคิดนี้ติดตั้งอยู่ภายในสำหรับจาวาคุณต้องมี "เวทมนต์" ตัวอย่างเช่น "commons-javaflow" จาก apache ใช้เช่น java ข้อเสียคือต้องใช้การแก้ไข bytecode แบบพิเศษในเวลาที่สร้าง ดังนั้นจึงควรใส่ทุกสิ่งไว้ในไลบรารีเพิ่มเติมซึ่งสร้างสคริปต์ที่กำหนดเอง


-1

โพสต์เก่า แต่อาจช่วยเหลือผู้อื่นใช้วิธีนี้:

OutputStream out = new ByteArrayOutputStream();
...
out.write();
...
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(out.toString().getBytes()));

1
ถึง String -> ปัญหาขนาด
user1594895

นอกจากนี้การเรียกtoString().getBytes()สตรีม * จะไม่ส่งคืนเนื้อหาของสตรีม
Maarten Bodewes

-1

แม้ว่าคุณจะไม่สามารถแปลง OutputStream ไปเป็น InputStream ได้ แต่ก็มีวิธีการใช้ PipedOutputStream และ PipedInputStream ซึ่งคุณสามารถมีข้อมูลที่เขียนไปยัง PipedOutputStream เพื่อให้สามารถใช้งานได้ผ่าน PipedInputStream ที่เกี่ยวข้อง
บางครั้งฉันต้องเผชิญกับสถานการณ์ที่คล้ายกันเมื่อจัดการกับห้องสมุดบุคคลที่สามที่ต้องการอินสแตนซ์ InputStream ที่จะส่งผ่านไปยังพวกเขาแทนที่จะเป็นอินสแตนซ์ OutputStream
วิธีที่ฉันแก้ไขปัญหานี้คือใช้ PipedInputStream และ PipedOutputStream
โดยวิธีที่พวกเขาใช้ยากและคุณต้องใช้มัลติเธรดเพื่อบรรลุสิ่งที่คุณต้องการ ฉันเพิ่งเผยแพร่การนำไปใช้กับ GitHub ซึ่งคุณสามารถใช้ได้
นี่คือการเชื่อมโยง คุณสามารถอ่านวิกิเพื่อเข้าใจวิธีใช้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.