2017-02-15 27 views
1

Я использую java.util.zip.Deflater для сжатия входящего потока данных, полученных через HTTP PUT, что означает, что я получаю данные быть сжатым в дрибах и драпировках, причем не все одновременно. Так что мой HTTP обработчик запросов повторно вызывает метод, который выглядит так, как данные поступают в: Вызов java.util.zip.Deflater.setInput несколько раз ничего не делает после первого раза

byte[] compress(byte[] input) { 
    byte[] output = null; 
    if (this.compressor == null) { 
     this.compressor = new Deflater(Deflater.BEST_COMPRESSION, true); 

     // Add gzip header: 
     output = getGzipHeader(); 
    } 

    this.compressor.setInput(input); 
    this.compressor.finish(); 

    while (!this.compressor.finished()) { 
     byte[] tempOutput = new byte[10240]; 

     int compressedLength = this.compressor.deflate(tempOutput); 

     if (output == null) { 
      output = Arrays.copyOf(tempOutput, compressedLength); 
     } else { 
      byte[] newOutput = Arrays.copyOf(output, output.length + compressedLength); 
      System.arraycopy(tempOutput, 0, newOutput, output.length, compressedLength); 
      output = newOutput; 
     } 
    } 

    // Update CRC: 
    this.crc.update(input); 
    this.byteCount += input.length; 

    return output 
} 

И конечно класс, содержащий этот метод имеет переменные экземпляра:

private Deflater compressor; 
private CRC32 crc = new CRC32(); 
private long byteCount = 0; 

А раз последние байты были получены из HTTP-запроса, я добавляю CRC и полную несжатую длину из переменных экземпляра crc и byteCount.

Пока я посылаю очень небольшое количество данных в HTTP PUT, это отлично работает, потому что метод compress вызван только один раз. Я получаю действительный файл gzip. Как только я отправляю более нескольких сотен байтов, в результате чего вызов вызываются более одного раза, он не работает, потому что при всех последующих вызовах compress после первого, this.compressor.finished() возвращает true, хотя я вызвал this.compressor.setInput(input) с новые входные данные. Если я просмотрю this.compressor.getBytesRead() после того, как все данные обработаны, значение, возвращаемое этим вызовом, будет точно размером первого входного буфера, который пришел (первый вызов this.compressor.setInput(input)). Ни один из последующих вызовов этого метода не увеличивает значение, возвращаемое getBytesRead().

Если я не звоню finish() после звонка на setInput(), он вообще не работает - я не получаю никакого вывода. Но похоже, что звонок finish() говорит Deflater не принимать любой больше ввода.

Что я делаю неправильно?

+1

это помогает: [DeflaterOutputStream] (http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/zip/DeflaterOutputStream.java#119)? (и было бы целесообразно просто использовать этот класс напрямую?) – jtahlborn

+0

Как сказал jtahlborn, DeflaterOutputStream очень поможет. Как общий комментарий, ваш код выглядел бы намного проще, если бы вы использовали 'ArrayList.addAll (Arrays.asList (tempOutput)) вместо того, чтобы делать все это манипулирование массивом самостоятельно. –

+0

@jtahlborn, нет, насколько я могу судить, для меня нет возможности использовать DeflatorOutputStream, потому что у меня нет выходного потока для записи.Вероятно, я должен был упомянуть, что моя служба построена с использованием [Vert.x] (http://vertx.io), поэтому я читаю из [ReadStream] (http://vertx.io/docs/apidocs/index .html? io/vertx/core/streams/ReadStream.html) и запись в [AsyncFile] (http://vertx.io/docs/apidocs/index.html?io/vertx/core/file/AsyncFile.html). Данные принимаются из ReadStream в байтовых буферах и записываются в AsyncFile в байтовых буферах. Нет входных или выходных потоков. –

ответ

0

Проблема решена. Я в основном скопировал код почти verbatiim от DeflaterOutputStream.write(...) в мой метод compress и скопировал с DeflaterOutputStream.close() в мой собственный метод finish, и он отлично работает.

Хитрость здесь не объясняется даже чуть-чуть в Javadoc для Deflater (а на самом деле противоречит пример кода, показанный в этом Javadoc) является то, что во время приема ввода и вызова deflater.setInput(...), вы проверяете только для !deflater.needsInput(), и не для !deflater.finished(). Затем только после того, как все входные данные получены, вы вызываете deflater.finish(), а затем, в решающей степени, обрабатываете все оставшиеся данные, которые могут быть отложены в буфере Deflater, путем циклирования while (!deflater.finished()) и продолжения дефлирования всех оставшихся данных.

Для получения дополнительной информации, просто откройте источник для DeflaterOutputStream и ознакомьтесь с его методами write(byte[] b, int off, int len) и finish().

 Смежные вопросы

  • Нет связанных вопросов^_^