2012-11-09 26 views
5

Ai đó có thể cho tôi biết làm thế nào để sao chép một inputstream, lấy thời gian tạo ít nhất có thể? Tôi cần phải sao chép một inputstream nhiều lần cho nhiều phương pháp để xử lý IS. Tôi đã thử ba cách và mọi thứ không hoạt động vì một lý do nào đó.Làm thế nào để sao chép một inputstream trong java trong thời gian tối thiểu

Phương pháp # 1: Nhờ cộng đồng stackoverflow, tôi thấy liên kết sau hữu ích và đã kết hợp đoạn mã trong chương trình của tôi.

How to clone an InputStream?

Tuy nhiên, sử dụng mã này có thể mất đến một phút (cho một tập tin 10MB) để tạo ra các inputstreams nhân bản và chương trình của tôi cần phải được càng nhanh càng tốt.

int read = 0; 
    byte[] bytes = new byte[1024*1024*2]; 

    ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
    while ((read = is.read(bytes)) != -1) 
     bos.write(bytes,0,read); 
    byte[] ba = bos.toByteArray(); 

    InputStream is1 = new ByteArrayInputStream(ba); 
    InputStream is2 = new ByteArrayInputStream(ba); 
    InputStream is3 = new ByteArrayInputStream(ba); 

Phương pháp # 2: Tôi cũng đã thử sử dụng BufferedInputStream để sao chép IS. Điều này nhanh (thời gian tạo chậm nhất == 1ms. Nhanh nhất == 0ms). Tuy nhiên, sau khi tôi gửi is1 để được xử lý, các phương thức xử lý is2 và is3 đã ném một lỗi nói rằng không có gì để xử lý, gần giống như tất cả 3 biến bên dưới tham chiếu cùng một IS.

is = getFileFromBucket(path,filename); 
    ... 
    ... 
    InputStream is1 = new BufferedInputStream(is); 
    InputStream is2 = new BufferedInputStream(is); 
    InputStream is3 = new BufferedInputStream(is); 

Phương pháp # 3: Tôi nghĩ trình biên dịch đang nói dối tôi. Tôi đã kiểm tra markSupported() cho is1 cho hai ví dụ trên. Nó trở thành sự thật vì vậy tôi nghĩ tôi có thể chạy

is1.mark() 
    is1.reset() 

hoặc chỉ

is1.reset(); 

trước khi đi qua các LÀ với các phương pháp tương ứng của tôi. Trong cả hai ví dụ trên, tôi nhận được thông báo lỗi là dấu hợp lệ.

Tôi không còn ý tưởng vì vậy cảm ơn trước vì bất kỳ trợ giúp nào bạn có thể cho tôi.

P.S. Từ những nhận xét tôi nhận được từ mọi người, tôi cần phải làm rõ một vài điều liên quan đến tình huống của mình: 1) Chương trình này đang chạy trên máy ảo 2) Dòng đầu vào đang được truyền vào tôi từ phương pháp khác. Tôi không đọc từ một tập tin địa phương 3) Kích thước của inputstream không biết

+3

Chạy mã cho Phương pháp # 1 mất 18 ms (đối với tệp 10 MB) trên máy tính của tôi. Có gì sai với phần cứng của bạn không? –

+0

Cảm ơn bạn đã trả lời. Tôi không nghĩ có gì sai với phần cứng của mình. Nó chỉ đánh tôi mà tôi quên đề cập đến 2 điều: a) đây là trên một máy ảo và b) inputstream là một tập tin jpg. Tốc độ nhanh nhất được thực hiện là 11 giây nhưng đánh dấu các bài kiểm tra của tôi, nó trung bình như 30 giây hoặc lâu hơn, chậm nhất là khoảng 1 phút (53 giây chính xác). – Classified

+1

Bạn có thể nhận được một tăng nhỏ nếu bạn làm điều này: byte [] ba = new byte [is.available()]; // Hoạt động nếu đó là FileInputStream DataInputStream mới (is) .readFully (ba); –

Trả lời

3

Tuy nhiên, sử dụng mã này có thể mất đến một phút (cho một tập tin 10MB) để tạo ra các inputstreams nhân bản và tôi chương trình cần phải nhanh nhất có thể.

Cũng sao chép luồng cần có thời gian và (nói chung) là cách duy nhất để sao chép luồng. Trừ khi bạn thắt chặt phạm vi của vấn đề, có rất ít cơ hội rằng hiệu suất có thể được cải thiện đáng kể.

Dưới đây là một vài trường hợp cải thiện có thể:

  • Nếu bạn biết trước số byte trong dòng sau đó bạn có thể đọc trực tiếp vào mảng byte thức.

  • Nếu bạn biết rằng dữ liệu đến từ một tệp, bạn có thể tạo bộ đệm được ánh xạ bộ nhớ cho tệp.

Nhưng vấn đề cơ bản là di chuyển nhiều byte xung quanh cần có thời gian. Và thực tế là mất 1 phút cho một tệp 10Mb (sử dụng mã trong Câu hỏi của bạn) gợi ý rằng nút cổ chai thực sự không có trong Java chút nào.

1

Bạn có dự định các phương pháp riêng biệt chạy song song hoặc tuần tự không? Nếu tuần tự, tôi thấy không có lý do gì để sao chép luồng đầu vào, vì vậy tôi phải giả sử bạn đang lập kế hoạch để xoay các luồng để quản lý từng luồng.

Hiện tại tôi không ở gần máy tính để kiểm tra điều này, nhưng tôi nghĩ bạn nên đọc đầu vào theo khối, nói 1024 byte và sau đó đẩy các khối đó (hoặc bản sao mảng của khối) vào luồng đầu ra của bạn với luồng đầu vào được đính kèm vào chuỗi của chúng. Có độc giả của bạn chặn nếu không có dữ liệu có sẵn, vv

+0

Cảm ơn bạn đã trả lời và đề xuất của bạn. Có, tôi dự định sử dụng đề tài ... một khi tôi tìm ra cách để sửa chữa cổ chai này. Tôi sẽ cố gắng để làm điều đó, đọc trong khối nhưng tôi nhận được dòng đầu vào thông qua từ phương pháp khác vì vậy tôi sẽ cần phải xem nếu nó khả thi trong trường hợp của tôi. – Classified

2

Về cách tiếp cận đầu tiên của bạn, là bao gồm trong việc đưa tất cả các byte của bạn trong một ByteArrayOutputStream:

  • Thứ nhất, phương pháp này tiêu tốn rất nhiều bộ nhớ. Nếu bạn không chắc chắn rằng JVM của bạn bắt đầu với đủ bộ nhớ được phân bổ, nó sẽ cần phải tự động yêu cầu bộ nhớ trong quá trình xử lý luồng của bạn và điều này là tốn thời gian.
  • ByteArrayOutputStream ban đầu được tạo với bộ đệm 32 byte. Mỗi khi bạn cố gắng để đặt một cái gì đó trong nó, nếu nó không phù hợp trong mảng byte hiện có một mảng lớn hơn mới được tạo ra và các byte cũ được sao chép vào một cái mới. Vì bạn đang sử dụng một đầu vào 2MB mỗi lần, bạn đang buộc ByteArrayOutputStream sao chép dữ liệu của nó hơn và hơn nữa vào mảng lớn hơn, tăng kích thước của mảng của nó trong 2MB mỗi lần.
  • Vì các mảng cũ là rác, có thể bộ nhớ của chúng đang được thu hồi bởi bộ thu gom rác, khiến quá trình sao chép của bạn thậm chí còn chậm hơn.
  • Có lẽ bạn nên xác định ByArrayOutputStream bằng cách sử dụng hàm tạo chỉ định kích thước bộ đệm ban đầu. Chính xác hơn khi bạn đặt kích thước thì quy trình càng nhanh càng tốt vì yêu cầu ít bản sao trung gian hơn.

Cách tiếp cận thứ hai là không có thật, bạn không thể trang trí cùng luồng đầu vào trong các luồng khác nhau và mong đợi mọi thứ hoạt động. Khi các byte được tiêu thụ bởi một luồng, luồng bên trong cũng bị cạn kiệt và không thể cung cấp các luồng khác với dữ liệu chính xác.

Trước khi tôi mở rộng câu trả lời cho phép tôi hỏi, các phương pháp khác của bạn có đang chờ nhận các bản sao của luồng đầu vào đang chạy trên một chuỗi riêng biệt không? Bởi vì nếu có, điều này nghe có vẻ giống như công việc cho PipedOutputStream và PipedInputStream?

+0

Cảm ơn bạn đã trả lời. Kể từ khi một phương pháp khác là đi qua inputstream với tôi tôi không biết kích thước của IS đến. Tôi chơi với làm cho mảng byte là 8MB nhưng nó vẫn mất một thời gian dài. Có người đề nghị tôi sử dụng BufferedInputStream và tôi đoán tôi đã không sử dụng nó một cách chính xác vì vậy xấu của tôi cho việc sử dụng không có thật =) Tôi có kế hoạch sử dụng các chủ đề cho các phương pháp khác của tôi vì vậy tôi sẽ xem xét đề nghị của bạn PipedIS và PipedOS để xem nếu nó giúp. Ngay bây giờ, tôi chỉ cố gắng để có được tất cả mọi thứ để làm việc serially trước khi tôi bắt đầu chơi với chủ đề. – Classified

6

cách sao chép luồng đầu vào, mất ít thời gian tạo nhất có thể? Tôi cần phải sao chép một inputstream nhiều lần cho nhiều phương pháp để xử lý các LÀ

Bạn chỉ có thể tạo ra một số loại một lớp tùy chỉnh ReusableInputStream trong đó bạn ngay cũng viết thư cho một nội ByteArrayOutputStream vào ngày 1 đầy đủ đọc, sau đó quấn nó trong một ByteBuffer khi byte cuối cùng được đọc và cuối cùng sử dụng lại cùng một ByteBuffer trên các lần đọc đầy đủ tiếp theo sẽ tự động bị lật khi đạt đến giới hạn. Điều này giúp bạn tiết kiệm từ một lần đọc đầy đủ như trong lần thử đầu tiên của bạn.

Dưới đây là một kickoff ví dụ cơ bản:

public class ReusableInputStream extends InputStream { 

    private InputStream input; 
    private ByteArrayOutputStream output; 
    private ByteBuffer buffer; 

    public ReusableInputStream(InputStream input) throws IOException { 
     this.input = input; 
     this.output = new ByteArrayOutputStream(input.available()); // Note: it's resizable anyway. 
    } 

    @Override 
    public int read() throws IOException { 
     byte[] b = new byte[1]; 
     read(b, 0, 1); 
     return b[0]; 
    } 

    @Override 
    public int read(byte[] bytes) throws IOException { 
     return read(bytes, 0, bytes.length); 
    } 

    @Override 
    public int read(byte[] bytes, int offset, int length) throws IOException { 
     if (buffer == null) { 
      int read = input.read(bytes, offset, length); 

      if (read <= 0) { 
       input.close(); 
       input = null; 
       buffer = ByteBuffer.wrap(output.toByteArray()); 
       output = null; 
       return -1; 
      } else { 
       output.write(bytes, offset, read); 
       return read; 
      } 
     } else { 
      int read = Math.min(length, buffer.remaining()); 

      if (read <= 0) { 
       buffer.flip(); 
       return -1; 
      } else { 
       buffer.get(bytes, offset, read); 
       return read; 
      } 
     } 

    } 

    // You might want to @Override flush(), close(), etc to delegate to input. 
} 

(lưu ý rằng công việc thực tế được thực hiện trong int read(byte[], int, int) thay vì trong int read() và do đó nó được dự kiến ​​sẽ được nhanh hơn khi người gọi bản thân cũng đang trình chiếu sử dụng một bộ đệm byte[])

Bạn có thể sử dụng nó như sau:

InputStream input = new ReusableInputStream(getFileFromBucket(path,filename)); 
IOUtils.copy(input, new FileOutputStream("/copy1.ext")); 
IOUtils.copy(input, new FileOutputStream("/copy2.ext")); 
IOUtils.copy(input, new FileOutputStream("/copy3.ext")); 

Về hiệu suất, 1 phút trên 10MB có nhiều khả năng là vấn đề về phần cứng chứ không phải vấn đề về phần mềm. Ổ cứng máy tính xách tay 7200rpm của tôi có thể hoạt động trong vòng chưa tới 1 giây.

+0

Cảm ơn đoạn mã. Tôi sẽ thử nó cùng với những gợi ý khác! – Classified

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