java中字符串数据的压缩与解压缩

java中字符串数据的压缩与解压缩,java,gzip,Java,Gzip,我使用下面的代码来压缩和解压字符串数据,但我面临的问题是,它很容易被压缩而没有错误,但解压方法会抛出以下错误 线程“main”java.io.IOException中的异常:不是GZIP格式 public static void main(String[] args) throws Exception { String string = "I am what I am hhhhhhhhhhhhhhhhhhhhhhhhhhhhh" + "bjgguj

我使用下面的代码来压缩和解压字符串数据,但我面临的问题是,它很容易被压缩而没有错误,但解压方法会抛出以下错误

线程“main”java.io.IOException中的异常:不是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;
     }

仍然无法解决此问题

问题在于这一行:

    String outStr = obj.toString("UTF-8");
字节数组
obj
包含任意二进制数据。不能像UTF-8那样“解码”任意二进制数据。如果您尝试,您将得到一个无法“编码”回字节的字符串。或者至少,你得到的字节将与你开始使用的不同。。。以至于它们不再是有效的GZIP流

修复方法是按原样存储或传输字节数组的内容。不要试图将其转换为字符串。它是二进制数据,而不是文本。

这是因为

String outStr = obj.toString("UTF-8");
发送
字节[]
,您可以从
ByteArrayOutputStream
获取该字节,并在
ByteArrayInputStream
中使用它来构造
GZIPInputStream
。以下是需要在代码中进行的更改

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

如果需要通过网络传输压缩内容或将其存储为文本,则必须使用Base64编码器(如apache commons codec Base64)将字节数组转换为Base64字符串,并在远程客户端将该字符串解码回字节数组。
在上找到了一个例子

上述答案解决了我们的问题,但除此之外。 如果我们试图解压缩未压缩的(“非zip格式”)字节[]。 我们将得到“非GZIP格式”异常消息

为了解决这个问题,我们可以在类中添加加法代码

public static boolean isCompressed(final byte[] compressed) {
    return (compressed[0] == (byte) (GZIPInputStream.GZIP_MAGIC)) && (compressed[1] == (byte) (GZIPInputStream.GZIP_MAGIC >> 8));
}
我的完整压缩类与压缩/解压缩类似:

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));
  }
}

无法将二进制数据转换为字符串。作为一种解决方案,您可以对二进制数据进行编码,然后将其转换为字符串。例如,看看这个

另一个正确压缩和解压缩的示例:

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

客户端发送一些需要压缩的消息,服务器(kafka)解压缩字符串

下面是我的示例:

压缩

公共静态字符串压缩(字符串str,字符串编码){
如果(str==null | | str.length()==0){
返回str;
}
试一试{
ByteArrayOutputStream out=新建ByteArrayOutputStream();
GZIPOutputStream gzip=新的GZIPOutputStream(输出);
write(str.getBytes(inencodeding));
gzip.close();
返回URLEncoder.encode(out.toString(“ISO-8859-1”),“UTF-8”);
}捕获(IOE异常){
e、 printStackTrace();
}
返回null;
}
解压缩:

publicstaticstring解压(stringstr,stringoutencode){
如果(str==null | | str.length()==0){
返回str;
}
试一试{
String decode=urldecker.decode(str,“UTF-8”);
ByteArrayOutputStream out=新建ByteArrayOutputStream();
ByteArrayInputStream in=新的ByteArrayInputStream(decode.getBytes(“ISO-8859-1”);
GZIPInputStream gunzip=新的GZIPInputStream(in);
字节[]缓冲区=新字节[256];
int n;
而((n=gunzip.read(buffer))>=0){
out.write(缓冲区,0,n);
}
return out.toString(outencoded);
}捕获(IOE异常){
e、 printStackTrace();
}
返回null;
}

<>代码>但是如果你想把压缩的数据存储为文本,你怎么能做到这一点?使用Base64,或者其他一些二进制作为文本编码。也考虑使用String Bu建器而不是仅仅串接字符串,因为字符串是不可变的,并且你会在String Po水池中浪费空间。(这件事泼了一盆又一盆的墨水)@fornarat,这句话毫无意义。除了字符串文本(即直接在.java文件中指定的事物,如字符串文本=“text”,放置在常量字符串池中)和字符串,您自己通过运行.In()方法将其具体放入该池中,在正常字符串对象中没有考虑“String Po水池”的空间。否则字符串就像其他东西一样只是一个对象。感谢您的努力,感谢您在运行的程序中发布问题。我遇到的最简单的Gzip示例。非常好。您缺少对
gzip.flush()的调用
before
gzip.close()虽然如此。我使用了您的代码,并在解压方法中添加了一点换行更正:“while((line=bufferedReader.readLine())!=null){outStr.append(line);outStr.append(System.getProperty(“line.separator”);}”在解压中,将while循环更改为:char[]buffer=new char[256];                 整数读取计数;虽然((readCount=bufferedReader.read(buffer))>0{outtr.append(buffer,0,readCount);}它不会因为换行符而混淆。如果您需要压缩值字符串(比如存储在DB中),一定要走这条路