কীভাবে ইনপুট স্ট্রিম ক্লোন করবেন?


162

আমার একটি ইনপুট স্ট্রিম রয়েছে যা আমি কিছু প্রক্রিয়াজাতকরণ করার পদ্ধতিতে পাস করি। আমি অন্য পদ্ধতিতে একই ইনপুটস্ট্রিম ব্যবহার করব, তবে প্রথম প্রক্রিয়াজাতকরণের পরে, ইনপুটস্ট্রিমটি পদ্ধতির অভ্যন্তরে বন্ধ হয়ে যায়।

তাকে বন্ধ করে দেওয়া পদ্ধতিতে আমি কীভাবে ইনপুট স্ট্রিমটি ক্লোন করতে পারি? এর আরেকটি সমাধান কি আছে?

সম্পাদনা করুন: যে পদ্ধতিগুলি ইনপুট স্ট্রিমটি বন্ধ করে দেয় সেগুলি হ'ল একটি বাহ্যিক পদ্ধতি। বন্ধ বা না করা সম্পর্কে আমার নিয়ন্ত্রণ নেই।

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";
    }
}

2
পদ্ধতিটি ফিরে আসার পরে আপনি কী স্ট্রিমটি "রিসেট" করতে চান? অর্থাৎ শুরু থেকেই স্ট্রিমটি পড়বেন?
আইওউব

হ্যাঁ, যে পদ্ধতিগুলি ইনপুট স্ট্রিমটি বন্ধ করে সেগুলি এনকোড করা চরসেটটি ফেরত দেয়। দ্বিতীয় পদ্ধতিটি হ'ল প্রথম পদ্ধতিতে পাওয়া চারসেট ব্যবহার করে ইনপুট স্ট্রিমটিকে একটি স্ট্রিতে রূপান্তর করা।
রেনাতো দিনহানি

সেক্ষেত্রে আপনি আমার উত্তরে যা বর্ণনা করছি তা করতে সক্ষম হবেন।
কাজ

আমি এটিকে সমাধান করার সর্বোত্তম উপায়টি জানি না, তবে আমি আমার সমস্যাটি অন্যথায় সমাধান করি। জেরিকো এইচটিএমএল পার্সারের মেথড টু স্ট্রিং স্ট্রিংটিকে সঠিক ফর্ম্যাটে ফর্ম্যাট করে। এই মুহুর্তে আমার দরকার।
রেনাতো দিনহানি

উত্তর:


188

আপনি যা করতে চান তা যদি একই তথ্য একাধিকবার পড়তে হয় এবং ইনপুট ডেটা মেমরির সাথে ফিট করার জন্য যথেষ্ট ছোট হয় তবে InputStreamআপনি কোনও বাইটআরআর আউটপুট স্ট্রিমে ডেটা অনুলিপি করতে পারেন ।

তারপরে আপনি বাইটগুলির সাথে সম্পর্কিত অ্যারেটি পেতে এবং আপনার পছন্দ মতো অনেকগুলি "ক্লোনড" বাইটআরআই ইনপুট স্ট্রিম খুলতে পারেন।

ByteArrayOutputStream baos = new ByteArrayOutputStream();

// Fake code simulating the copy
// You can generally do better with nio if you need...
// And please, unlike me, do something about the Exceptions :D
byte[] buffer = new byte[1024];
int len;
while ((len = input.read(buffer)) > -1 ) {
    baos.write(buffer, 0, len);
}
baos.flush();

// Open new InputStreams using the recorded bytes
// Can be repeated as many times as you wish
InputStream is1 = new ByteArrayInputStream(baos.toByteArray()); 
InputStream is2 = new ByteArrayInputStream(baos.toByteArray()); 

তবে নতুন ডেটা পাওয়ার জন্য যদি আপনার সত্যিকারের মূল স্ট্রিমটি খোলার প্রয়োজন হয়, তবে আপনাকে এই বাহ্যিক close()পদ্ধতিটি ট্র্যাক করতে হবে এবং এটি কোনওভাবে বলা থেকে বাধা দিতে হবে।

আপডেট (2019):

জাভা 9 সাল থেকে মধ্য বিটগুলি এর সাথে প্রতিস্থাপন করা যেতে পারে InputStream.transferTo:

ByteArrayOutputStream baos = new ByteArrayOutputStream();
input.transferTo(baos);
InputStream firstClone = new ByteArrayInputStream(baos.toByteArray()); 
InputStream secondClone = new ByteArrayInputStream(baos.toByteArray()); 

আমি আমার সমস্যার আর একটি সমাধান খুঁজে পাই যাতে ইনপুটস্ট্রিম অনুলিপি করা জড়িত না, তবে আমি মনে করি যদি আমার ইনপুটস্ট্রিমের অনুলিপি প্রয়োজন হয় তবে এটিই সেরা সমাধান।
রেনাতো দিনহানি

7
এই পদ্ধতির ইনপুট স্ট্রিমের সম্পূর্ণ সামগ্রীর সমানুপাতিক স্মৃতি গ্রহণ করে। উন্নত ব্যবহার করতে TeeInputStreamবেশি উত্তর বর্ণনা অনুযায়ী এখানে
আইয়ুব

2
আইউইটিস (অ্যাপাচি কমন্স থেকে) এর একটি অনুলিপি পদ্ধতি রয়েছে যা আপনার কোডের মাঝখানে বাফার পড়তে / লিখতে পারে।
রিহ্যাব

31

আপনি অ্যাপাচি এর ব্যবহার করতে চান CloseShieldInputStream:

এটি এমন একটি মোড়ক যা স্ট্রিমটি বন্ধ হতে বাধা দেবে। আপনি এই জাতীয় কিছু করবেন।

InputStream is = null;

is = getStream(); //obtain the stream 
CloseShieldInputStream csis = new CloseShieldInputStream(is);

// call the bad function that does things it shouldn't
badFunction(csis);

// happiness follows: do something with the original input stream
is.read();

দেখতে দুর্দান্ত, তবে এখানে কাজ করে না। আমি কোড সহ আমার পোস্টটি সম্পাদনা করব।
রেনাতো দিনহানি

CloseShieldকাজ করছে না কারণ আপনার আসল HttpURLConnectionইনপুট স্ট্রিমটি কোথাও বন্ধ রয়েছে। আপনার পদ্ধতিতে সুরক্ষিত স্ট্রিমের সাথে আইওউটিলগুলি কল করা উচিত নয় IOUtils.toString(csContent,charset)?
অ্যান্টনি অ্যাকলিওলি

হতে পারে এটি হতে পারে। আমি কি এইচটিপিআরএল URL সংযোগ বন্ধ হতে পারি?
রেনাতো দিনহানি

1
@Renato। হয়তো সমস্যাটি একেবারেই close()কল নয়, তবে স্ট্রিমটি শেষ পর্যন্ত পড়া হচ্ছে। যেহেতু mark()এবং reset()HTTP সংযোগের জন্য সেরা পদ্ধতি নাও হতে পারে, সম্ভবত আপনার আমার উত্তরে বর্ণিত বাইট অ্যারে পদ্ধতির দিকে নজর দেওয়া উচিত।
অ্যান্টনি অ্যাকসিওলি

1
আরও একটি জিনিস, আপনি সর্বদা একই ইউআরএলটিতে একটি নতুন সংযোগ খুলতে পারেন। এখানে দেখুন: stackoverflow.com/questions/5807340/...
এন্থনি Accioly

11

আপনি এটি ক্লোন করতে পারবেন না এবং আপনি কীভাবে আপনার সমস্যার সমাধান করতে চলেছেন তা নির্ভর করে ডেটার উত্সটি কী।

একটি সমাধান হ'ল ইনপুটস্ট্রিম থেকে সমস্ত ডেটা বাইট অ্যারেতে পড়ুন এবং তারপরে সেই বাইট অ্যারের চারপাশে একটি বাইটআররেআইপুট স্ট্রিম তৈরি করুন এবং সেই ইনপুট স্ট্রিমটি আপনার পদ্ধতিতে পাস করুন।

সম্পাদনা 1: অর্থাত্ যদি অন্য পদ্ধতিতেও একই ডেটা পড়তে হয়। অর্থাৎ আপনি স্ট্রিমটি "রিসেট" করতে চান।


আপনার কোন অংশে সাহায্যের দরকার তা আমি জানি না। আমার ধারনা আপনি কোন স্ট্রিম থেকে পড়তে জানেন? ইনপুটস্ট্রিম থেকে সমস্ত ডেটা পড়ুন এবং বাইটআরআউটআউটপুট স্ট্রিমে ডেটা লিখুন। আপনি সমস্ত ডেটা পড়া শেষ করার পরে বাইটআরআউটপুট স্ট্রিমে বাইটআররে () এ কল করুন। তারপরে সেই বাইট অ্যারেটি বাইটআরআরআইপুট স্ট্রিমের কনস্ট্রাক্টরে পাস করুন।
কাজ 21

8

স্ট্রিম থেকে পড়া ডেটা যদি বড় হয় তবে আমি অ্যাপাচি কমন্স আইও থেকে একটি টিআইপুট স্ট্রিম ব্যবহার করার পরামর্শ দেব। এইভাবে আপনি প্রয়োজনীয়ভাবে ইনপুটটিকে প্রতিলিপি করতে পারেন এবং আপনার ক্লোন হিসাবে একটি টি পাইপ পাস করতে পারেন।


5

এটি সমস্ত পরিস্থিতিতে কাজ নাও করতে পারে তবে আমি যা করেছি তা এখানে: বাহ্যিক লিব ডেটা পড়ার সাথে সাথে আমি ফিল্টার ইনপুটস্ট্রিম ক্লাসটি বাড়িয়েছি এবং বাইটগুলির প্রয়োজনীয় প্রসেসিং করি।

public class StreamBytesWithExtraProcessingInputStream extends FilterInputStream {

    protected StreamBytesWithExtraProcessingInputStream(InputStream in) {
        super(in);
    }

    @Override
    public int read() throws IOException {
        int readByte = super.read();
        processByte(readByte);
        return readByte;
    }

    @Override
    public int read(byte[] buffer, int offset, int count) throws IOException {
        int readBytes = super.read(buffer, offset, count);
        processBytes(buffer, offset, readBytes);
        return readBytes;
    }

    private void processBytes(byte[] buffer, int offset, int readBytes) {
       for (int i = 0; i < readBytes; i++) {
           processByte(buffer[i + offset]);
       }
    }

    private void processByte(int readByte) {
       // TODO do processing here
    }

}

তারপরে আপনি কেবল StreamBytesWithExtraProcessingInputStreamইনপুট স্ট্রিমে কোথায় গিয়েছেন তার একটি উদাহরণ দিয়ে যান। কনস্ট্রাক্টর প্যারামিটার হিসাবে মূল ইনপুট স্ট্রিম সহ।

এটি লক্ষ করা উচিত যে এটি বাইট জন্য বাইট কাজ করে, তাই উচ্চ কার্যকারিতা প্রয়োজন হলে এটি ব্যবহার করবেন না।


3

UPD। এর আগে মন্তব্যটি দেখুন। এটি ঠিক যা বলা হয়েছিল তা নয়।

আপনি যদি ব্যবহার করেন তবে apache.commonsআপনি স্ট্রিমগুলি ব্যবহার করে অনুলিপি করতে পারেন IOUtils

আপনি নিম্নলিখিত কোড ব্যবহার করতে পারেন:

InputStream = IOUtils.toBufferedInputStream(toCopy);

আপনার পরিস্থিতির জন্য উপযুক্ত উপযুক্ত উদাহরণ এখানে:

public void cloneStream() throws IOException{
    InputStream toCopy=IOUtils.toInputStream("aaa");
    InputStream dest= null;
    dest=IOUtils.toBufferedInputStream(toCopy);
    toCopy.close();
    String result = new String(IOUtils.toByteArray(dest));
    System.out.println(result);
}

এই কোডটির কিছু নির্ভরতা দরকার:

ম্যাভেন

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.4</version>
</dependency>

GRADLE

'commons-io:commons-io:2.4'

এই পদ্ধতির জন্য এখানে ডিওসি রেফারেন্স রয়েছে:

একটি ইনপুট স্ট্রিমের পুরো বিষয়বস্তু নিয়ে আসে এবং ফলাফল ইনপুট স্ট্রিম হিসাবে একই ডেটা উপস্থাপন করে। এই পদ্ধতিটি দরকারী যেখানে,

উত্স ইনপুট স্ট্রিম ধীর। এর সাথে নেটওয়ার্ক সংস্থান যুক্ত রয়েছে, তাই আমরা এটিকে দীর্ঘ সময়ের জন্য উন্মুক্ত রাখতে পারি না। এর সাথে নেটওয়ার্কের সময়সীমা যুক্ত রয়েছে।

আপনার সম্পর্কে আরো জানতে পারেন IOUtilsএখানে: http://commons.apache.org/proper/commons-io/javadocs/api-2.4/org/apache/commons/io/IOUtils.html#toBufferedInputStream(java.io.InputStream)


7
এটি ইনপুট স্ট্রিমটিকে ক্লোন করে না তবে কেবল এটি বাফার করে। এটি একই নয়; ওপি একই স্ট্রিমটি পুনরায় পড়তে চায় (এর একটি অনুলিপি)।
রাফেল

1

কোটলিনের সাথে সমাধানটি নীচে দেওয়া হল।

আপনি নিজের ইনপুট স্ট্রিমটি বাইটআরেতে অনুলিপি করতে পারেন

val inputStream = ...

val byteOutputStream = ByteArrayOutputStream()
inputStream.use { input ->
    byteOutputStream.use { output ->
        input.copyTo(output)
    }
}

val byteInputStream = ByteArrayInputStream(byteOutputStream.toByteArray())

আপনার যদি byteInputStreamএকাধিকবার byteInputStream.reset()পড়তে হয় তবে আবার পড়ার আগে কল করুন ।

https://code.luasoftware.com/tutorials/kotlin/how-to-clone-inputstream/


0

নীচের ক্লাসটি কৌশলটি করা উচিত। কেবল একটি উদাহরণ তৈরি করুন, "গুণ" পদ্ধতিটি কল করুন এবং উত্স ইনপুট স্ট্রিম এবং আপনার প্রয়োজনীয় নকলের পরিমাণ সরবরাহ করুন।

গুরুত্বপূর্ণ: আপনাকে অবশ্যই পৃথক থ্রেডে সমস্ত ক্লোন করা স্ট্রিম একই সাথে গ্রাস করতে হবে।

package foo.bar;

import java.io.IOException;
import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class InputStreamMultiplier {
    protected static final int BUFFER_SIZE = 1024;
    private ExecutorService executorService = Executors.newCachedThreadPool();

    public InputStream[] multiply(final InputStream source, int count) throws IOException {
        PipedInputStream[] ins = new PipedInputStream[count];
        final PipedOutputStream[] outs = new PipedOutputStream[count];

        for (int i = 0; i < count; i++)
        {
            ins[i] = new PipedInputStream();
            outs[i] = new PipedOutputStream(ins[i]);
        }

        executorService.execute(new Runnable() {
            public void run() {
                try {
                    copy(source, outs);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });

        return ins;
    }

    protected void copy(final InputStream source, final PipedOutputStream[] outs) throws IOException {
        byte[] buffer = new byte[BUFFER_SIZE];
        int n = 0;
        try {
            while (-1 != (n = source.read(buffer))) {
                //write each chunk to all output streams
                for (PipedOutputStream out : outs) {
                    out.write(buffer, 0, n);
                }
            }
        } finally {
            //close all output streams
            for (PipedOutputStream out : outs) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

প্রশ্নের উত্তর দেয় না। চরসেট নির্ধারণ করতে তিনি একটি পদ্ধতিতে প্রবাহটি ব্যবহার করতে চান এবং তারপরে দ্বিতীয় পদ্ধতিতে এর চরসেট সহ এটি পুনরায় পড়তে চান।
লার্নের মারকুইস

0

কোনও ইনপুট স্ট্রিম ক্লোনিং করা ভাল ধারণা নাও হতে পারে, কারণ ইনপুট স্ট্রিমটি ক্লোন করা হওয়ার বিবরণ সম্পর্কে গভীর জ্ঞান প্রয়োজন। এর জন্য একটি কার্যপ্রণালী হ'ল নতুন ইনপুট স্ট্রিম তৈরি করা যা একই উত্স থেকে আবার পড়ে।

সুতরাং কিছু জাভা 8 বৈশিষ্ট্য ব্যবহার করে এটি দেখতে এরকম হবে:

public class Foo {

    private Supplier<InputStream> inputStreamSupplier;

    public void bar() {
        procesDataThisWay(inputStreamSupplier.get());
        procesDataTheOtherWay(inputStreamSupplier.get());
    }

    private void procesDataThisWay(InputStream) {
        // ...
    }

    private void procesDataTheOtherWay(InputStream) {
        // ...
    }
}

এই পদ্ধতিটির ইতিবাচক প্রভাব রয়েছে যে এটি ইতিমধ্যে বিদ্যমান কোডটি পুনরায় ব্যবহার করবে - ইনপুট সংযুক্ত ইনপুট স্ট্রিমের তৈরি inputStreamSupplier । এবং স্ট্রিমের ক্লোনিংয়ের জন্য দ্বিতীয় কোডের পথ বজায় রাখার দরকার নেই।

অন্যদিকে, যদি স্ট্রিম থেকে পড়া ব্যয়বহুল হয় (কারণ এটি কম ব্যান্ডউইথ সংযোগের মাধ্যমে সম্পন্ন হয়), তবে এই পদ্ধতিটি ব্যয় দ্বিগুণ করবে। এটি কোনও নির্দিষ্ট সরবরাহকারী ব্যবহার করে সংশোধন করা যেতে পারে যা প্রথমে স্থানীয়ভাবে স্ট্রিমের সামগ্রী সংরক্ষণ করবে এবং InputStreamএখনকার স্থানীয় সংস্থার জন্য এটি সরবরাহ করবে।


এই উত্তরটি আমার কাছে পরিষ্কার নয়। আপনি বিদ্যমান থেকে সরবরাহকারীকে কীভাবে সূচনা করবেন is?
ব্যবহারকারী 1156544

@ user1156544 আমি যেমন লিখেছি একটি ইনপুট স্ট্রিম ক্লোনিং করা ভাল ধারণা নাও হতে পারে, কারণ ইনপুট স্ট্রিমটির ক্লোন হওয়ার বিবরণ সম্পর্কে গভীর জ্ঞান প্রয়োজন। আপনি সরবরাহকারীকে বিদ্যমান ইনভার স্ট্রিম সীমানা তৈরি করতে পারবেন না। সরবরাহকারী প্রতিটিবার যখনই অনুরোধ করা হয় তখন একটি নতুন ইনপুট স্ট্রিম তৈরি করতে এটি java.io.Fileবা java.net.URLউদাহরণ ব্যবহার করতে পারে ।
স্পেসট্রকার

আমি এখন দেখি. ওপি স্পষ্টভাবে জিজ্ঞাসা করে এটি ইনপুটস্প্রিমের সাথে কাজ করবে না, তবে ফাইল বা URL- এর সাথে যদি সেগুলি ডেটার মূল উত্স হয়। ধন্যবাদ
ব্যবহারকারী 1156544
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.