2010-09-03 27 views
15

chúng tôi đang làm việc trên một chương trình mà chúng tôi cần phải tuôn ra (buộc nén và gửi dữ liệu) GZIPOutputStream. Vấn đề là, phương pháp xả của GZIPOutputStream không hoạt động như mong đợi (buộc nén và gửi dữ liệu), thay vào đó, Luồng chờ đợi nhiều dữ liệu hơn để nén dữ liệu hiệu quả.Lực lượng tuôn ra trên GZIPOutputStream trong java

Khi bạn gọi xong dữ liệu được nén và được gửi qua luồng đầu ra nhưng GZIPOutputStream (không phải luồng bên dưới) sẽ bị đóng để chúng tôi không thể ghi thêm dữ liệu cho đến khi tạo GZIPOutputStream mới, chi phí thời gian và hiệu suất.

Hy vọng mọi người đều có thể trợ giúp về vấn đề này.

Trân trọng.

+0

Tôi đang làm việc với Java6. – Hemeroc

Trả lời

8

Tôi đã không cố gắng này nêu ra, và lời khuyên này sẽ không hữu ích cho đến khi chúng tôi có Java 7 trong tay, nhưng tài liệu cho GZIPOutputStream 's flush() phương pháp kế thừa từ DeflaterOutputStream dựa vào các chế độ tuôn ra quy định tại xây dựng thời gian với the syncFlush argument (liên quan đến Deflater#SYNC_FLUSH) để quyết định có xóa dữ liệu đang chờ xử lý được nén hay không. Đối số syncFlush này cũng được chấp nhận bởi GZIPOutputStream vào thời gian xây dựng.

Có vẻ như bạn muốn sử dụng một trong hai Deflator#SYNC_FLUSH hoặc thậm chí có Deflater#FULL_FLUSH, nhưng, trước khi đào xuống mà đến nay, lần đầu tiên thử làm việc với the two-argument hoặc the four-argument GZIPOutputStream constructor và vượt qua true cho lập luận syncFlush. Điều đó sẽ kích hoạt hành vi xả nước mà bạn mong muốn.

+0

Xin chào, câu trả lời của bạn là tuyệt vời nếu bạn đang làm việc với Java7 mà không được phát hành vào lúc này. Tôi đang làm việc với java6 (như hầu hết người dùng làm). – Hemeroc

+0

Ồ, tôi xin lỗi về điều đó. Bạn nói đúng: những chữ ký này chưa có sẵn trong Java 6. Điều đó giúp tôi có quyền đọc tài liệu "mới nhất". Chúng ta sẽ phải đợi những thứ này đến. – seh

1

Bug ID 4813885 xử lý sự cố này. Nhận xét về "DamonHD", được gửi vào ngày 9 tháng 9 năm 2006 (khoảng nửa chừng báo cáo lỗi) chứa ví dụ về FlushableGZIPOutputStream mà ông đã xây dựng trên đầu trang của Jazzlib'snet.sf.jazzlib.DeflaterOutputStream.

Để tham khảo, đây là một (định dạng lại) chiết xuất:

/** 
* Substitute for GZIPOutputStream that maximises compression and has a usable 
* flush(). This is also more careful about its output writes for efficiency, 
* and indeed buffers them to minimise the number of write()s downstream which 
* is especially useful where each write() has a cost such as an OS call, a disc 
* write, or a network packet. 
*/ 
public class FlushableGZIPOutputStream extends net.sf.jazzlib.DeflaterOutputStream { 
    private final CRC32 crc = new CRC32(); 
    private final static int GZIP_MAGIC = 0x8b1f; 
    private final OutputStream os; 

    /** Set when input has arrived and not yet been compressed and flushed downstream. */ 
    private boolean somethingWritten; 

    public FlushableGZIPOutputStream(final OutputStream os) throws IOException { 
     this(os, 8192); 
    } 

    public FlushableGZIPOutputStream(final OutputStream os, final int bufsize) throws IOException { 
     super(new FilterOutputStream(new BufferedOutputStream(os, bufsize)) { 
      /** Suppress inappropriate/inefficient flush()es by DeflaterOutputStream. */ 
      @Override 
      public void flush() { 
      } 
     }, new net.sf.jazzlib.Deflater(net.sf.jazzlib.Deflater.BEST_COMPRESSION, true)); 
     this.os = os; 
     writeHeader(); 
     crc.reset(); 
    } 

    public synchronized void write(byte[] buf, int off, int len) throws IOException { 
     somethingWritten = true; 
     super.write(buf, off, len); 
     crc.update(buf, off, len); 
    } 

    /** 
    * Flush any accumulated input downstream in compressed form. We overcome 
    * some bugs/misfeatures here so that: 
    * <ul> 
    * <li>We won't allow the GZIP header to be flushed on its own without real compressed 
    * data in the same write downstream. 
    * <li>We ensure that any accumulated uncompressed data really is forced through the 
    * compressor. 
    * <li>We prevent spurious empty compressed blocks being produced from successive 
    * flush()es with no intervening new data. 
    * </ul> 
    */ 
    @Override 
    public synchronized void flush() throws IOException { 
     if (!somethingWritten) { return; } 

     // We call this to get def.flush() called, 
     // but suppress the (usually premature) out.flush() called internally. 
     super.flush(); 

     // Since super.flush() seems to fail to reliably force output, 
     // possibly due to over-cautious def.needsInput() guard following def.flush(), 
     // we try to force the issue here by bypassing the guard. 
     int len; 
     while((len = def.deflate(buf, 0, buf.length)) > 0) { 
      out.write(buf, 0, len); 
     } 

     // Really flush the stream below us... 
     os.flush(); 

     // Further flush()es ignored until more input data data written. 
     somethingWritten = false; 
    } 

    public synchronized void close() throws IOException { 
     if (!def.finished()) { 
      def.finish(); 
      do { 
       int len = def.deflate(buf, 0, buf.length); 
       if (len <= 0) { 
        break; 
       } 
       out.write(buf, 0, len); 
      } while (!def.finished()); 
     } 

     // Write trailer 
     out.write(generateTrailer()); 

     out.close(); 
    } 

    // ... 
} 

Bạn có thể tìm thấy nó hữu ích.

+0

Xin chào, tôi mong đợi những vấn đề tương tự như nardian, bất kỳ đề xuất nào? – Hemeroc

9

Tôi không tìm thấy câu trả lời khác để làm việc. Nó vẫn từ chối tuôn ra vì mã nguồn gốc mà GZIPOutputStream đang sử dụng lưu giữ trên dữ liệu.

Rất may, tôi đã phát hiện ra rằng ai đó đã triển khai FlushableGZIPOutputStream như một phần của dự án Tomcat của Apache. Dưới đây là phần kỳ diệu:

@Override 
public synchronized void flush() throws IOException { 
    if (hasLastByte) { 
     // - do not allow the gzip header to be flushed on its own 
     // - do not do anything if there is no data to send 

     // trick the deflater to flush 
     /** 
     * Now this is tricky: We force the Deflater to flush its data by 
     * switching compression level. As yet, a perplexingly simple workaround 
     * for 
     * http://developer.java.sun.com/developer/bugParade/bugs/4255743.html 
     */ 
     if (!def.finished()) { 
      def.setLevel(Deflater.NO_COMPRESSION); 
      flushLastByte(); 
      flagReenableCompression = true; 
     } 
    } 
    out.flush(); 
} 

Bạn có thể tìm thấy toàn bộ lớp trong jar này (nếu bạn sử dụng Maven):

<dependency> 
    <groupId>org.apache.tomcat</groupId> 
    <artifactId>tomcat-coyote</artifactId> 
    <version>7.0.8</version> 
</dependency> 

Hoặc chỉ cần đi và lấy mã nguồn FlushableGZIPOutputStream.java

Đó là được phát hành theo giấy phép Apache-2.0.

+1

Tôi nhận được 'java.lang.IllegalStateException: setLevel không thể được gọi sau khi setInput' trong Android – 18446744073709551615

+0

nhận được nó:' new GZIPOutputStream (...) {{setSyncFlush (true);}}; '(dấu ngoặc kép vì nó là một khởi tạo thể hiện bên trong một lớp không tên) – 18446744073709551615

0

Có cùng vấn đề với số Android. Câu trả lời Accepter không hoạt động vì def.setLevel(Deflater.NO_COMPRESSION); ném ngoại lệ. Theo phương pháp flush, nó thay đổi mức độ nén của Deflater. Vì vậy, tôi cho rằng việc thay đổi nén nên được gọi trước khi ghi dữ liệu, nhưng tôi không chắc chắn.

Như vậy là 2 lựa chọn khác:

  • nếu mức API của ứng dụng của bạn là cao hơn 19 thì bạn có thể cố gắng sử dụng constructor with syncFlush param
  • giải pháp khác là sử dụng jzlib.
+0

jzlib đã không làm việc cho tôi – 18446744073709551615

1

Mã này hoạt động tốt cho tôi trong đơn đăng ký của tôi.

public class StreamingGZIPOutputStream extends GZIPOutputStream { 

    public StreamingGZIPOutputStream(OutputStream out) throws IOException { 
     super(out); 
    } 

    @Override 
    protected void deflate() throws IOException { 
     // SYNC_FLUSH is the key here, because it causes writing to the output 
     // stream in a streaming manner instead of waiting until the entire 
     // contents of the response are known. for a large 1 MB json example 
     // this took the size from around 48k to around 50k, so the benefits 
     // of sending data to the client sooner seem to far outweigh the 
     // added data sent due to less efficient compression 
     int len = def.deflate(buf, 0, buf.length, Deflater.SYNC_FLUSH); 
     if (len > 0) { 
      out.write(buf, 0, len); 
     } 
    } 

} 
+0

Tôi đã có cùng một vấn đề chính xác, và điều này giải quyết vấn đề của tôi độc đáo! (việc truyền trực tuyến tới máy khách tốt hơn để bắt đầu sớm hơn) – Tony

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