2009-05-14 17 views
8

Tôi đã có một dịch vụ web Java trong JAX-WS trả về một OutputStream từ một phương thức khác. Tôi không thể tìm ra cách để luồng OutputStream vào DataHandler được trả về theo bất kỳ cách nào khác ngoài việc tạo một tệp tạm thời, ghi vào nó, sau đó mở nó trở lại như một InputStream. Dưới đây là ví dụ:Làm thế nào để bạn có thể tạo một OutputStream thành một StreamingDataHandler?

@MTOM 
@WebService 
class Example { 
    @WebMethod 
    public @XmlMimeType("application/octet-stream") DataHandler service() { 
     // Create a temporary file to write to 
     File fTemp = File.createTempFile("my", "tmp"); 
     OutputStream out = new FileOutputStream(fTemp); 

     // Method takes an output stream and writes to it 
     writeToOut(out); 
     out.close(); 

     // Create a data source and data handler based on that temporary file 
     DataSource ds = new FileDataSource(fTemp); 
     DataHandler dh = new DataHandler(ds); 
     return dh; 
    } 
} 

Vấn đề chính là phương thức writeToOut() có thể trả về dữ liệu lớn hơn nhiều so với bộ nhớ của máy tính. Đó là lý do tại sao phương pháp này đang sử dụng MTOM ngay từ đầu - để truyền dữ liệu. Tôi dường như không thể quấn đầu xung quanh cách truyền dữ liệu trực tiếp từ OutputStream mà tôi cần cung cấp cho DataHandler được trả về (và cuối cùng là máy khách, người nhận được StreamingDataHandler).

Tôi đã thử chơi với PipedInputStream và PipedOutputStream, nhưng những thứ đó dường như không hoàn toàn là những gì tôi cần, vì DataHandler cần được trả về sau khi PipedOutputStream được ghi vào.

Bất kỳ ý tưởng nào?

+0

Xem thêm [câu hỏi này] (http: // stackoverflow.com/questions/2830561/how-to-convert-an-inputstream-to-a-datahandler) – schnatterer

Trả lời

4

tôi đã tìm ra câu trả lời, dọc theo dòng mà Christian đã nói về (tạo một thread mới để thực hiện writeToOut()):

@MTOM 
@WebService 
class Example { 
    @WebMethod 
    public @XmlMimeType("application/octet-stream") DataHandler service() { 
     // Create piped output stream, wrap it in a final array so that the 
     // OutputStream doesn't need to be finalized before sending to new Thread. 
     PipedOutputStream out = new PipedOutputStream(); 
     InputStream in = new PipedInputStream(out); 
     final Object[] args = { out }; 

     // Create a new thread which writes to out. 
     new Thread(
      new Runnable(){ 
       public void run() { 
        writeToOut(args); 
        ((OutputStream)args[0]).close(); 
       } 
      } 
     ).start(); 

     // Return the InputStream to the client. 
     DataSource ds = new ByteArrayDataSource(in, "application/octet-stream"); 
     DataHandler dh = new DataHandler(ds); 
     return dh; 
    } 
} 

Đó là một chút phức tạp hơn do final biến, nhưng như xa như tôi có thể nói điều này là chính xác. Khi chủ đề được bắt đầu, nó sẽ chặn khi lần đầu tiên cố gọi out.write(); đồng thời, luồng đầu vào được trả lại cho máy khách, người đã mở khóa ghi bằng cách đọc dữ liệu. (Vấn đề với các giải pháp trước đây của tôi về giải pháp này là tôi đã không đóng đúng luồng đó và do đó gặp phải lỗi.)

+0

Tôi thực sự không biết nhiều về điều này, nhưng hãy chắc chắn rằng các đường ống * Stream là một trong hai luồng an toàn hoặc sử dụng từ khóa "đồng bộ" mà tôi không quen thuộc. – Christian

+0

Xin lưu ý rằng [javadoc của 'javax.mail.util.ByteArrayDataSource'] (http://docs.oracle.com/javaee/6/api/javax/mail/util/ByteArrayDataSource.html#ByteArrayDataSource%28java.io .InputStream,% 20java.lang.String% 29) nói rằng 'InputStream' được đọc vào bộ nhớ hoàn toàn khi xây dựng. Điều này có thể dẫn đến 'OutOfMemoryError' khi xử lý các tệp lớn – schnatterer

1

Mẫu trình bao bọc? :-).

Tuỳ chỉnh javax.activation.DataSource triển khai (chỉ 4 phương pháp) để có thể thực hiện việc này?

return new DataHandler(new DataSource() { 
    // implement getOutputStream to return the stream used inside writeToOut() 
    ... 
}); 

Tôi không có IDE để kiểm tra điều này vì vậy tôi chỉ đang thực hiện đề xuất. Tôi cũng sẽ cần bố trí chung writeToOut :-).

+0

Có thể tìm thấy cách thực hiện làm việc trong [câu trả lời này] (http://stackoverflow.com/a/10783565/1845976). – schnatterer

3

Xin lỗi, tôi chỉ làm điều này cho C# chứ không phải java, nhưng tôi nghĩ phương pháp của bạn nên khởi chạy một chuỗi để chạy "writeToOut (out);" trong parralel. Bạn cần phải tạo một luồng đặc biệt và chuyển nó vào luồng mới để cung cấp luồng đó cho writeToOut. Sau khi bắt đầu luồng, bạn trả về đối tượng luồng đó cho người gọi của mình.

Nếu bạn chỉ có phương thức ghi vào luồng và trả về sau đó và phương pháp khác tiêu thụ luồng và trả về sau đó, không có cách nào khác.

Một phần khó khăn là giữ một luồng an toàn đa luồng như vậy: Nó sẽ chặn mỗi bên nếu bộ đệm trong quá đầy.

Không biết liệu luồng luồng Java có hoạt động cho điều đó hay không.

+0

+1 - Ý tưởng là chính xác, và tôi cảm ơn bạn đã đưa tôi đi đúng hướng, nhưng câu trả lời của tôi có giải pháp Java thực tế mà tôi muốn những người tương lai có thể tìm thấy đầu tiên nếu họ gặp phải vấn đề tương tự. –

0

Trong ứng dụng của mình, tôi sử dụng InputStreamDataSource thực hiện lấy InputStream làm đối số hàm tạo thay vì Tệp trong FileDataSource . Nó hoạt động cho đến nay.

public class InputStreamDataSource implements DataSource { 

ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 
private final String name; 

public InputStreamDataSource(InputStream inputStream, String name) { 
    this.name = name; 
    try { 
     int nRead; 
     byte[] data = new byte[16384]; 
     while ((nRead = inputStream.read(data, 0, data.length)) != -1) { 
      buffer.write(data, 0, nRead); 
     } 

     buffer.flush(); 
     inputStream.close(); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 

} 

@Override 
public String getContentType() { 
    return new MimetypesFileTypeMap().getContentType(name); 
} 

@Override 
public InputStream getInputStream() throws IOException { 
    return new ByteArrayInputStream(buffer.toByteArray()); 
} 

@Override 
public String getName() { 
    return name; 
} 

@Override 
public OutputStream getOutputStream() throws IOException { 
    throw new IOException("Read-only data"); 
} 

}

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