OutputStream से InputStream बनाने का सबसे कुशल तरीका


84

यह पृष्ठ: http://blog.ostermiller.org/convert-java-outputstream-inputstream वर्णन करता है कि आउटपुटस्ट्रीम से एक इनपुटस्ट्रीम कैसे बनाया जाए:

new ByteArrayInputStream(out.toByteArray())

अन्य विकल्प पाइपेडस्ट्रीम और नए थ्रेड्स का उपयोग करना है जो बोझिल है।

मुझे मेमोरी बाइट सरणी में कई मेगाबाइट को नए में कॉपी करने का विचार पसंद नहीं है। वहाँ एक पुस्तकालय है कि यह और अधिक कुशलता से करता है?

संपादित करें:

लॉरेंस गोंसाल्वेस की सलाह से, मैंने पिपेडस्ट्रीम की कोशिश की और यह पता चला कि वे इससे निपटने के लिए उतने कठिन नहीं हैं। यहाँ क्लोजर में नमूना कोड है:

(defn #^PipedInputStream create-pdf-stream [pdf-info]
  (let [in-stream (new PipedInputStream)
        out-stream (PipedOutputStream. in-stream)]
    (.start (Thread. #(;Here you write into out-stream)))
    in-stream))

जवाबों:


72

यदि आप एक बार में सभी डेटा को इन-मेमोरी बफर में कॉपी नहीं करना चाहते हैं, तो आपको अपना कोड होना चाहिए जो आउटपुटस्ट्रीम (निर्माता) और इनपुटस्ट्रीम (उपभोक्ता) का उपयोग करने वाले कोड का उपयोग करता है ) या तो एक ही धागे में वैकल्पिक करें, या दो अलग-अलग थ्रेड में समवर्ती रूप से संचालित करें। उन्हें एक ही धागे में संचालित करना संभवतः अधिक जटिल है कि दो अलग-अलग थ्रेड्स का उपयोग करना, बहुत अधिक त्रुटि प्रवण है (आपको यह सुनिश्चित करने की आवश्यकता होगी कि उपभोक्ता इनपुट के इंतजार में कभी ब्लॉक नहीं करता है, या आप प्रभावी रूप से गतिरोध करेंगे) और आवश्यकता होगी उत्पादक और उपभोक्ता के पास एक ही लूप चल रहा है जो कि बहुत अधिक युग्मित है।

इसलिए एक दूसरे सूत्र का उपयोग करें। यह वास्तव में जटिल नहीं है। आप जिस पेज से जुड़े हैं, उसका एक आदर्श उदाहरण है:

  PipedInputStream in = new PipedInputStream();
  PipedOutputStream out = new PipedOutputStream(in);
  new Thread(
    new Runnable(){
      public void run(){
        class1.putDataOnOutputStream(out);
      }
    }
  ).start();
  class2.processDataFromInputStream(in);

मुझे लगता है कि आपको प्रत्येक उपभोक्ता थ्रेड के लिए नया PipedInputStream बनाने की भी आवश्यकता है। यदि आप दूसरे धागे से पाइप से पढ़ते हैं, तो यह आपको एक त्रुटि देगा।
डेनिस तुलस्किए

@ लॉरेंस: मैं 2 सूत्र का उपयोग करने के लिए आपके औचित्य को नहीं समझता ... कम से कम यह एक आवश्यकता है कि InputStream से पढ़े गए सभी वर्ण आउटपुटस्ट्रीम को समयबद्ध तरीके से लिखे गए हैं।
स्टीफन सी

8
स्टीफन: जब तक यह लिखा नहीं गया है तब तक आप कुछ नहीं पढ़ सकते हैं। तो केवल एक धागे के साथ आपको या तो सबकुछ लिखने की ज़रूरत है (एक बड़ा इन-मेमोरी एरे बनाते हुए जिसे वैगिफ़ टालना चाहते थे) या आपको उन्हें वैकल्पिक रूप से रखने की ज़रूरत है बहुत सावधानी बरतते हुए पाठक कभी भी इनपुट का इंतज़ार न करें (क्योंकि अगर वह ऐसा करता है लेखक को कभी भी निष्पादन नहीं मिलेगा)।
लॉरेंस गोन्साल्व्स

1
क्या यह सुझाव एक जेईई वातावरण में उपयोग करने के लिए सुरक्षित है जहां कंटेनर शायद अपने स्वयं के थ्रेड का एक बहुत चल रहा है?
टॉस्कन

2
@Toskan यदि new Threadकिसी भी कारण से आपके कंटेनर में उपयुक्त नहीं है, तो देखें कि क्या कोई थ्रेड पूल है जिसका आप उपयोग कर सकते हैं।
लारेंस गोंसाल्वेस

14

ईज़ीस्ट्रीम नामक एक और ओपन सोर्स लाइब्रेरी है जो पारदर्शी तरीके से पाइप और धागे से संबंधित है। अगर सबकुछ ठीक हो जाता है तो यह वास्तव में जटिल नहीं है। समस्या तब उत्पन्न होती है जब (लारेंस गोंसाल्व्स उदाहरण को देखते हुए)

(बाहर) class1.putDataOnOutputStream;

एक अपवाद फेंकता है। उस उदाहरण में धागा बस पूरा हो जाता है और अपवाद खो जाता है, जबकि बाहरी InputStreamको छोटा किया जा सकता है।

Easystream अपवाद प्रचार और अन्य खराब समस्याओं से निपटता है जो मैं लगभग एक वर्ष से डिबगिंग कर रहा हूं। (मैं पुस्तकालय का मैंटर हूं: जाहिर है कि मेरा समाधान सबसे अच्छा है;)) यहां इसका उपयोग कैसे किया जाए, इस पर एक उदाहरण है:

final InputStreamFromOutputStream<String> isos = new InputStreamFromOutputStream<String>(){
 @Override
 public String produce(final OutputStream dataSink) throws Exception {
   /*
    * call your application function who produces the data here
    * WARNING: we're in another thread here, so this method shouldn't 
    * write any class field or make assumptions on the state of the outer class. 
    */
   return produceMydata(dataSink)
 }
};

एक अच्छा परिचय भी है जहाँ एक OutputStream को InputStream में बदलने के अन्य सभी तरीके बताए गए हैं। देखने लायक।


1
उनकी कक्षा का उपयोग करने का ट्यूटोरियल code.google.com/p/io-tools/wiki/Tutorial_EasyStream
koppor

9

बफर को कॉपी करने से बचने वाला एक सरल उपाय एक विशेष उद्देश्य बनाना है ByteArrayOutputStream:

public class CopyStream extends ByteArrayOutputStream {
    public CopyStream(int size) { super(size); }

    /**
     * Get an input stream based on the contents of this output stream.
     * Do not use the output stream after calling this method.
     * @return an {@link InputStream}
     */
    public InputStream toInputStream() {
        return new ByteArrayInputStream(this.buf, 0, this.count);
    }
}

आवश्यकतानुसार उपरोक्त आउटपुट स्ट्रीम में लिखें, फिर toInputStreamअंतर्निहित बफर पर एक इनपुट स्ट्रीम प्राप्त करने के लिए कॉल करें । उस बिंदु के बाद बंद के रूप में आउटपुट स्ट्रीम पर विचार करें।


7

मुझे लगता है कि InputStream को OutputStream से कनेक्ट करने का सबसे अच्छा तरीका पाइप्ड स्ट्रीम के माध्यम से है - java.io पैकेज में उपलब्ध:

// 1- Define stream buffer
private static final int PIPE_BUFFER = 2048;

// 2 -Create PipedInputStream with the buffer
public PipedInputStream inPipe = new PipedInputStream(PIPE_BUFFER);

// 3 -Create PipedOutputStream and bound it to the PipedInputStream object
public PipedOutputStream outPipe = new PipedOutputStream(inPipe);

// 4- PipedOutputStream is an OutputStream, So you can write data to it
// in any way suitable to your data. for example:
while (Condition) {
     outPipe.write(mByte);
}

/*Congratulations:D. Step 4 will write data to the PipedOutputStream
which is bound to the PipedInputStream so after filling the buffer
this data is available in the inPipe Object. Start reading it to
clear the buffer to be filled again by the PipedInputStream object.*/

मेरी राय में इस कोड के दो मुख्य लाभ हैं:

1 - बफर के अलावा मेमोरी की कोई अतिरिक्त खपत नहीं है।

2 - आपको मैन्युअल रूप से डेटा कतारबद्ध को संभालने की आवश्यकता नहीं है


1
यह बहुत बढ़िया होगा, लेकिन जावेदोक्स का कहना है कि यदि आप इन्हें उसी धागे में पढ़ते और लिखते हैं तो आपको गतिरोध मिल सकता है। काश, उन्होंने NIO के साथ इसे अपडेट किया होता!
नैट ग्लेन

1

मैं आमतौर पर गतिरोध की बढ़ती संभावना, कोड को समझने की बढ़ती कठिनाई और अपवादों से निपटने की समस्याओं के कारण एक अलग धागा बनाने से बचने की कोशिश करता हूं।

यहाँ मेरा प्रस्तावित समाधान है: एक ProducerInputStream, जो बार-बार कॉल करके चंक्स में सामग्री बनाता है।

public abstract class ProducerInputStream extends InputStream {

    private ByteArrayInputStream bin = new ByteArrayInputStream(new byte[0]);
    private ByteArrayOutputStream bout = new ByteArrayOutputStream();

    @Override
    public int read() throws IOException {
        int result = bin.read();
        while ((result == -1) && newChunk()) {
            result = bin.read();
        }
        return result;
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        int result = bin.read(b, off, len);
        while ((result == -1) && newChunk()) {
            result = bin.read(b, off, len);
        }
        return result;
    }

    private boolean newChunk() {
        bout.reset();
        produceChunk(bout);
        bin = new ByteArrayInputStream(bout.toByteArray());
        return (bout.size() > 0);
    }

    public abstract void produceChunk(OutputStream out);

}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.