2013-07-13 33 views
5

Deflater.setLevel() không hoạt động như mong đợi đối với tôi.Công cụ Deflater.setLevel() có hoạt động không?

static void test1() throws Exception { 
    byte[] output = new byte[20]; 
    Deflater compresser = new Deflater(); 
    // compresser.setLevel(Deflater.BEST_COMPRESSION); 
    compresser.setInput("blah".getBytes("UTF-8")); 
    compresser.finish(); 
    int len = compresser.deflate(output); 
    System.out.println("len="+ len+ " " +Arrays.toString(output)); 
} 

Các công trình trên ok cho tôi (Java 7), nhưng khi tôi bỏ ghi chú dòng compresser.setLevel(), nó phá vỡ (deflate() trả về 0 byte). Điều tương tự cũng xảy ra với mọi cấp độ nén, ngoại trừ DEFAULT. Cụ thể hơn, nó chỉ "hoạt động" (thay vào đó, nó vô hại) khi thiết lập mức là như nhau đã được thiết lập (explictly hoặc ngầm), như ở đây) trong constructor - đó là để nói, nó chỉ có thể được sử dụng khi nó vô dụng.

Xem ví dụ tại Ideone.

This question điểm cho cùng một vấn đề và câu trả lời được chấp nhận về cơ bản cho biết: không đặt mức với setter, làm điều đó trong hàm tạo. Xa vời, IMO - tại sao setLevel() lại tồn tại? Là nó bị hỏng hoặc là chúng ta thiếu một cái gì đó?

+0

Nó có vẻ là một lỗi trong API 'Deflater' ... Đọc javadoc, không rõ ràng về những gì' .finish() 'thực sự làm. Cách giải quyết được đề xuất trong công việc trả lời cuối cùng? – fge

Trả lời

5

Tôi đào một ít vào mã nguồn JDK. Nó thực sự thiết lập mức độ. Nếu bạn theo dõi setLevel() với compresser.deflate(new byte[0]);, thì nó sẽ hoạt động.

Điều gì xảy ra là cuộc gọi đầu tiên của deflate() sau khi setLevel() sẽ thấy rằng cấp độ được thay đổi và gọi hàm zlib deflateParams() để thay đổi. deflateParams() sau đó sẽ nén dữ liệu có sẵn, nhưng thực tế bạn đã yêu cầu finish() không được chuyển. Việc thực hiện JDK sau đó không gọi deflate() với Z_FINISH. Kết quả là, dữ liệu bạn đưa ra được gửi để nén, máy nén tích lũy dữ liệu, nhưng nó không phát ra một khối nén vì nó không được yêu cầu hoàn thành. Vì vậy, bạn không có gì.

Bạn cần gọi đến deflate() sau setLevel() để thực sự đặt cấp. Sau đó, dữ liệu tiếp theo sẽ được nén với cấp độ mới. Điều quan trọng cần lưu ý là dữ liệu được cung cấp cho cuộc gọi deflate() đầu tiên sau khi số setLevel() sẽ được nén bằng mức nén cũ. Chỉ dữ liệu được cung cấp sau đó cuộc gọi deflate() sẽ sử dụng cấp độ mới. Vì vậy, nếu trong ví dụ của bạn, bạn chỉ cần thực hiện một số deflate() sau lần cuối cùng, nó sẽ áp dụng finish() và bạn sẽ nhận được dữ liệu nén của mình, nhưng nó sẽ sử dụng mức nén mặc định.

+0

"thực tế là bạn yêu cầu cho một kết thúc() không được thông qua cùng" Đó sẽ là một lỗi, phải không? – leonbloy

+0

Tôi sẽ nói có. Đặc biệt là vì tôi không thấy hành vi đó được ghi lại ở đâu. –

3

Tôi nghi ngờ đây là lỗi.

Nếu bạn nhìn vào source code, bạn sẽ thấy rằng chỉ trong constructor sao họ gọi phương thức bản địa initthực đặt mức độ nén. Dường như mức độ nén phải được đặt trước khi bất kỳ cuộc gọi init gốc nào diễn ra .ie Trước khi tạo đối tượng Deflater.

setLevel(int) chỉ đặt mức cho đối tượng bề ngoài. Không có cuộc gọi nào đến thư viện gốc.

+0

Nó sẽ đủ tệ đến nỗi 'setLevel()' là một NOP trong ngụy trang (cố định trong ... [1998] (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4152809)), nhưng nó tệ hơn, có vẻ như phá vỡ sự giải nén. – leonbloy

+0

Cuộc gọi tiếp theo của 'deflate()' đặt mức. –