web-dev-qa-db-fra.com

compression et décompression de données de chaîne en java

J'utilise le code suivant pour compresser et décompresser des données de chaîne, mais le problème auquel je suis confronté est qu'il est facilement compressé sans erreur, mais la méthode de décompression renvoie l'erreur suivante. 

Exception dans le fil "principal" Java.io.IOException: Pas au format 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;
     }

Je ne comprenais toujours pas comment résoudre ce problème !!!

34
rampuriyaaa

C'est à cause de 

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

Envoyez le byte[] que vous pouvez obtenir de votre ByteArrayOutputStream et utilisez-le tel quel dans votre ByteArrayInputStream pour construire votre GZIPInputStream. Vous trouverez ci-dessous les modifications à apporter à votre code.

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));
    ...
}
35
SudoRahul

La réponse ci-dessus résout notre problème mais en plus de cela. si nous essayons de décompresser un octet [] ..__ non compressé ("pas un format Zip"), nous aurons un message d’exception "Pas au format GZIP".

Pour résoudre ce problème, nous pouvons ajouter du code d'addition dans notre classe.

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

Ma classe de compression complète avec compression/décompression ressemblerait à ceci:

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));
  }
}
23
Arun Pratap Singh

Si vous avez besoin de transférer le contenu compressé via le réseau ou de le stocker sous forme de texte, vous devez utiliser un encodeur Base64 (tel que le codec Apache commons Base64) pour convertir le tableau d'octets en chaîne Base64 et client distant. Vous avez trouvé un exemple dans Utilisez Zip Stream et Base64 Encoder pour compresser des données de grande taille !

12
JeffersonZhang

Le problème est cette ligne:

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

Le tableau d'octets obj contient des données binaires arbitraires. Vous ne pouvez pas "décoder" des données binaires arbitraires comme s'il s'agissait d'UTF-8. Si vous essayez, vous obtiendrez une chaîne qui ne peut pas ensuite être "encodée" en octets. Ou du moins, les octets que vous obtiendrez seront différents de ceux avec lesquels vous avez commencé ... dans la mesure où ils ne sont plus un flux GZIP valide.

Le correctif consiste à stocker ou à transmettre le contenu du tableau d'octets tel quel. N'essayez pas de le convertir en chaîne. Ce sont des données binaires, pas du texte.

7
Stephen C

Un autre exemple de compression et décompression correcte:

@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);
        }
    }
}
1
mrserfr

Vous ne pouvez pas convertir les données binaires en String. En tant que solution, vous pouvez encoder des données binaires puis les convertir en chaîne. Par exemple, regardez ceci Comment convertissez-vous des données binaires en chaînes et en Java?

0
Andrey Badaev