2013-05-03 17 views
31

Korzystam z poniższego kodu do kompresji i dekompresji ciągów danych, ale problemem, z którym się spotykam jest to, że łatwo się skompresuje bez błędu, ale metoda dekompresji wyrzuca następujący błąd.kompresja i dekompresja ciągów danych w java

Wyjątek w wątku „głównym” java.io.IOException: Nie w formacie GZIP

public static void main(String[] args) throws Exception { 
     String string = "I am what I am hhhhhhhhhhhhhhhhhhhhhhhhhhhhh" 
       + "bjggujhhhhhhhhh" 
       + "rggggggggggggggggggggggggg" 
       + "esfffffffffffffffffffffffffffffff" 
       + "esffffffffffffffffffffffffffffffff" 
       + "esfekfgy enter code here`etd`enter code here wdd" 
       + "heljwidgutwdbwdq8d" 
       + "skdfgysrdsdnjsvfyekbdsgcu" 
       +"jbujsbjvugsduddbdj"; 

     System.out.println("after compress:"); 
     String compressed = compress(string); 
     System.out.println(compressed); 
     System.out.println("after decompress:"); 
     String decomp = decompress(compressed); 
     System.out.println(decomp); 
    } 


    public static String compress(String str) throws Exception { 
     if (str == null || str.length() == 0) { 
      return str; 
     } 
     System.out.println("String length : " + str.length()); 
     ByteArrayOutputStream obj=new ByteArrayOutputStream(); 
     GZIPOutputStream gzip = new GZIPOutputStream(obj); 
     gzip.write(str.getBytes("UTF-8")); 
     gzip.close(); 
     String outStr = obj.toString("UTF-8"); 
     System.out.println("Output String length : " + outStr.length()); 
     return outStr; 
    } 

     public static String decompress(String str) throws Exception { 
     if (str == null || str.length() == 0) { 
      return str; 
     } 
     System.out.println("Input String length : " + str.length()); 
     GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(str.getBytes("UTF-8"))); 
     BufferedReader bf = new BufferedReader(new InputStreamReader(gis, "UTF-8")); 
     String outStr = ""; 
     String line; 
     while ((line=bf.readLine())!=null) { 
      outStr += line; 
     } 
     System.out.println("Output String lenght : " + outStr.length()); 
     return outStr; 
    } 

Nadal nie mógł dowiedzieć się, jak rozwiązać ten problem !!!

+1

wysiłku doceniane i dziękuję za komentarz na pytanie o program. –

Odpowiedz

30

Wynika to z

String outStr = obj.toString("UTF-8"); 

Wyślij byte[] które można uzyskać od ByteArrayOutputStream i używać go jako takie w swojej ByteArrayInputStream skonstruować swój GZIPInputStream. Poniżej znajdują się zmiany, które należy wprowadzić w kodzie.

byte[] compressed = compress(string); //In the main method 

public static byte[] compress(String str) throws Exception { 
    ... 
    ... 
    return obj.toByteArray(); 
} 

public static String decompress(byte[] bytes) throws Exception { 
    ... 
    GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(bytes)); 
    ... 
} 
+9

Należy również rozważyć użycie StringBuilder zamiast tylko konkatenacji String, ponieważ String jest niezmienny, a marnujesz miejsce w poolpoole.(Rozlały się atramenty) – fornarat

7

Problemem jest to linia:

String outStr = obj.toString("UTF-8"); 

Tablica bajtów obj zawiera dowolne dane binarne. Nie można "dekodować" dowolnych danych binarnych, tak jakby były w formacie UTF-8. Jeśli spróbujesz, otrzymasz String, który nie może być "zakodowany" z powrotem do bajtów. A przynajmniej, otrzymane bajty będą inne od tego, z czym zacząłeś ... w stopniu, w jakim nie są już prawidłowym strumieniem GZIP.

Poprawka służy do przechowywania lub przesyłania zawartości tablicy bajtów bez zmian. Nie próbuj przekonwertować go na ciąg. Jest to dane binarne, a nie tekst.

+0

Jeśli jednak chcesz zapisać skompresowane dane jako tekst, jak możesz to osiągnąć? – perrohunter

+3

Użyj base64 lub innego kodowania binarnego jako tekstu. –

11

Jeśli kiedykolwiek zajdzie potrzeba przeniesienia spakowany treści za pośrednictwem sieci lub zapisać go jako tekst, trzeba użyć Base64 kodera (takich jak Apache Commons kodeka Base64) przekonwertować tablicę bajtów do Base64 String, i dekodowania ciąg znaków do tablicy bajtów na zdalnym kliencie. Znaleziono przykład pod adresem Use Zip Stream and Base64 Encoder to Compress Large String Data!

+0

dobry przykład, jeśli potrzebujesz struny w wyniku – demon101

+0

Najprostszy przykład, z jakim się zetknąłem podczas gzipowania. –

17

Powyższa odpowiedź rozwiązuje nasz problem, ale oprócz tego. jeśli próbujemy rozpakować nieskompresowany ("nie format zip") bajt []. otrzymamy komunikat o błędzie "Nie w formacie GZIP".

Aby rozwiązać ten problem, możemy dodać kod dodatkowy do naszej klasy.

public static boolean isCompressed(final byte[] compressed) { 
    return (compressed[0] == (byte) (GZIPInputStream.GZIP_MAGIC)) && (compressed[1] == (byte) (GZIPInputStream.GZIP_MAGIC >> 8)); 
} 

mój pełny z klasy kompresji/dekompresji kompresji będzie wyglądać:

import java.io.BufferedReader; 
import java.io.ByteArrayInputStream; 
import java.io.ByteArrayOutputStream; 
import java.io.IOException; 
import java.io.InputStreamReader; 
import java.util.zip.GZIPInputStream; 
import java.util.zip.GZIPOutputStream; 

public class GZIPCompression { 
    public static byte[] compress(final String str) throws IOException { 
    if ((str == null) || (str.length() == 0)) { 
     return null; 
    } 
    ByteArrayOutputStream obj = new ByteArrayOutputStream(); 
    GZIPOutputStream gzip = new GZIPOutputStream(obj); 
    gzip.write(str.getBytes("UTF-8")); 
    gzip.flush(); 
    gzip.close(); 
    return obj.toByteArray(); 
    } 

    public static String decompress(final byte[] compressed) throws IOException { 
    final StringBuilder outStr = new StringBuilder(); 
    if ((compressed == null) || (compressed.length == 0)) { 
     return ""; 
    } 
    if (isCompressed(compressed)) { 
     final GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(compressed)); 
     final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(gis, "UTF-8")); 

     String line; 
     while ((line = bufferedReader.readLine()) != null) { 
     outStr.append(line); 
     } 
    } else { 
     outStr.append(compressed); 
    } 
    return outStr.toString(); 
    } 

    public static boolean isCompressed(final byte[] compressed) { 
    return (compressed[0] == (byte) (GZIPInputStream.GZIP_MAGIC)) && (compressed[1] == (byte) (GZIPInputStream.GZIP_MAGIC >> 8)); 
    } 
} 
+0

bardzo ładne. brakuje ci połączenia z 'gzip.flush();' before 'gzip.close();'. – isapir

+2

dzięki za przejrzenie kodu. –

+0

używam kodu i dodać trochę korekcji dla linii przerwie, w swojej metody dekompresji: „while ((linia = bufferedReader.readLine())! = Null) { \t \t \t \t outStr.append (linia); \t \t \t \t outStr.append (System.getProperty ("line.separator")); \t \t \t} " –

0

Innym przykładem poprawnej kompresji i dekompresji:

@Slf4j 
public class GZIPCompression { 
    public static byte[] compress(final String stringToCompress) { 
     if (isNull(stringToCompress) || stringToCompress.length() == 0) { 
      return null; 
     } 

     try (final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
      final GZIPOutputStream gzipOutput = new GZIPOutputStream(baos)) { 
      gzipOutput.write(stringToCompress.getBytes(UTF_8)); 
      gzipOutput.finish(); 
      return baos.toByteArray(); 
     } catch (IOException e) { 
      throw new UncheckedIOException("Error while compression!", e); 
     } 
    } 

    public static String decompress(final byte[] compressed) { 
     if (isNull(compressed) || compressed.length == 0) { 
      return null; 
     } 

     try (final GZIPInputStream gzipInput = new GZIPInputStream(new ByteArrayInputStream(compressed)); 
      final StringWriter stringWriter = new StringWriter()) { 
      IOUtils.copy(gzipInput, stringWriter, UTF_8); 
      return stringWriter.toString(); 
     } catch (IOException e) { 
      throw new UncheckedIOException("Error while decompression!", e); 
     } 
    } 
} 
Powiązane problemy