2009-08-04 33 views
73

trang này: http://blog.ostermiller.org/convert-java-outputstream-inputstream mô tả làm thế nào để tạo ra một InputStream từ OutputStream:Cách hiệu quả nhất để tạo ra InputStream từ OutputStream

new ByteArrayInputStream(out.toByteArray()) 

lựa chọn thay thế khác là sử dụng PipedStreams và chủ đề mới đó là cồng kềnh.

Tôi không thích ý tưởng sao chép nhiều megabyte sang mảng byte mới. Có thư viện nào thực hiện điều này hiệu quả hơn không?

EDIT:

Bằng lời khuyên từ Laurence Gonsalves, tôi đã cố gắng PipedStreams và nó bật ra họ không phải là khó khăn để giải quyết. Dưới đây là đoạn code mẫu trong clojure:

(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)) 

Trả lời

63

Nếu bạn không muốn sao chép tất cả các dữ liệu vào một vùng đệm trong bộ nhớ cùng một lúc sau đó bạn sẽ cần phải có mã của bạn có sử dụng các OutputStream (nhà sản xuất) và mã sử dụng InputStream (người tiêu dùng) hoặc là thay thế trong cùng một luồng, hoặc hoạt động đồng thời trong hai luồng riêng biệt. Việc chúng hoạt động trong cùng một luồng có thể phức tạp hơn nhiều khi sử dụng hai luồng riêng biệt, dễ bị lỗi hơn nhiều (bạn cần phải đảm bảo rằng người tiêu dùng không bao giờ là khối chờ đầu vào, hoặc bạn sẽ bế tắc hiệu quả) và sẽ đòi hỏi phải có nhà sản xuất và người tiêu dùng chạy trong cùng một vòng lặp mà dường như quá chặt chẽ với nhau.

Vì vậy, hãy sử dụng chuỗi thứ hai. Nó thực sự không phải là phức tạp. Trang web bạn liên kết đến đã có một ví dụ hoàn hảo:

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

Tôi nghĩ bạn cũng cần phải tạo PipedInputStream mới cho mỗi chủ đề người tiêu dùng. Nếu bạn đọc từ ống từ một sợi khác, nó sẽ cho bạn một lỗi. –

+0

@Lawrence: Tôi không hiểu lý do của bạn để sử dụng 2 chủ đề ... KHÔNG CẦN nó là một yêu cầu tất cả các ký tự đọc từ InputStream được ghi vào OutputStream một cách kịp thời. –

+0

Thx. Tôi bỏ qua PipedStreams lúc đầu bởi vì tôi nghĩ rằng nó sẽ là quá cồng kềnh để đối phó với họ. Hóa ra không có gì lớn cả, đặc biệt là từ clojure. –

15

Có một thư viện mã nguồn mở gọi EasyStream rằng thỏa thuận với đường ống và chủ đề một cách minh bạch. Điều đó không thực sự phức tạp nếu mọi thứ suôn sẻ. Các vấn đề phát sinh khi (xem ví dụ Laurence Gonsalves)

class1.putDataOnOutputStream (out);

Ném ngoại lệ. Trong ví dụ đó, chỉ đơn giản là hoàn thành và ngoại lệ bị mất, trong khi bên ngoài InputStream có thể bị cắt ngắn.

Giao dịch Easystream với tuyên truyền ngoại lệ và các vấn đề khó chịu khác mà tôi đã gỡ lỗi trong khoảng một năm. (Tôi là mantainer của thư viện: rõ ràng là giải pháp của tôi là tốt nhất;)) Dưới đây là một ví dụ về cách sử dụng nó:

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

Ngoài ra còn có một đẹp introduction nơi mà tất cả những cách khác để chuyển đổi một OutputStream vào một InputStream được giải thích. Đáng để có một cái nhìn.

+1

Hướng dẫn sử dụng lớp học của họ có sẵn tại https://code.google.com/p/io-tools/wiki/Tutorial_EasyStream – koppor

+0

Công việc tuyệt vời! Nó thực sự giải quyết vấn đề của tôi. – smartwjw

4

Tôi nghĩ cách tốt nhất để kết nối InputStream với OutputStream là thông qua luồng đường ống - có sẵn trong java.io gói, như sau:

// 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.*/ 

Theo tôi có hai ưu điểm chính cho mã này:

1 - Không có tiêu thụ thêm bộ nhớ trừ bộ đệm.

2 - Bạn không cần phải xử lý dữ liệu xếp hàng tự

+1

Điều này sẽ rất tuyệt vời, nhưng [javadocs] (http://docs.oracle.com/javase/7/docs/api/java/io/PipedInputStream.html) nói rằng nếu bạn đọc và viết thư cho chúng trong cùng một bạn có thể bị bế tắc. Tôi ước họ đã cập nhật thông tin này với NIO! –

6

Một giải pháp đơn giản mà tránh sao chép bộ đệm là để tạo ra một mục đích đặc biệt 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); 
    } 
} 

Viết thư cho các dòng sản lượng trên khi cần, sau đó gọi toInputStream để lấy luồng đầu vào trên bộ đệm cơ bản. Xem xét luồng đầu ra khi đóng sau thời điểm đó.

0

Tôi thường cố gắng tránh tạo ra một chuỗi riêng biệt vì cơ hội bế tắc tăng, độ khó tăng của việc hiểu mã và các vấn đề về xử lý ngoại lệ.

Đây là giải pháp của tôi đề xuất: a ProducerInputStream tạo ra nội dung trong khối bởi các cuộc gọi lặp đi lặp lại để produceChunk():

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); 

} 
Các vấn đề liên quan