无法正确转换 Base64 字符串和解压缩

Can not convert Base64 String and unGzip it properly

我有 Base64 字符串。我正在尝试解码它,然后解压缩它。

String textToDecode = "H4sIAAAAAAAAAAEgAN//0JTQtdGC0LDQu9C40LfQuNGA0L7QstCw0L3QvdGL0LmRCuyiIAAAAA==\n";
byte[] data = Base64.decode(textToDecode, Base64.DEFAULT);
String result = GzipUtil.decompress(data);

我用来解压的代码:

public static String decompress(byte[] compressed) throws IOException {
    final int BUFFER_SIZE = 32;
    ByteArrayInputStream is = new ByteArrayInputStream(compressed);
    GZIPInputStream gis = new GZIPInputStream(is, BUFFER_SIZE);
    StringBuilder string = new StringBuilder();
    byte[] data = new byte[BUFFER_SIZE];
    int bytesRead;
    while ((bytesRead = gis.read(data)) != -1) {
        string.append(new String(data, 0, bytesRead));
    }
    gis.close();
    is.close();
    return string.toString();
}

我应该得到这个字符串:

Детализированный

结果,我得到了这个带有问号符号的字符串:

Детализирован��ый

我的错误是什么?以及如何解决?

可能是这里的问题:

  string.append(new String(data, 0, bytesRead))

您正在使用默认字符编码将字节解码为 Java 字符串。如果(当前)默认编码与将原始字符编码为字节(压缩之前等)时使用的编码不同,那么您可能会得到无法正确解码的字节。然后解码器将用解码器的替换字符替换它们;即默认 '\uFFFD'

如果这是问题所在,那么解决方案是找出正确的字符编码并使用 String(byte[], int, int, Charset) 创建 String.

一个问题是,当从字节转换为字符串(内部 Unicode)时 没有给出编码。对于像 UTF-8 这样的多字节编码,不能采用固定数量的字节(如 32),然后在最后得到一个有效序列。

您显然丢失了一半序列。因此编码可能是UTF-8。

final int BUFFER_SIZE = 32;
ByteArrayInputStream is = new ByteArrayInputStream(compressed);
GZIPInputStream gis = new GZIPInputStream(is, BUFFER_SIZE);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] data = new byte[BUFFER_SIZE];
int bytesRead;
while ((bytesRead = gis.read(data)) != -1) {
    baos.write(data, 0, bytesRead);
}
gis.close();
return baos.toString("UTF-8"); // Or "Windows-1251" ...

上面解决了buffer boundary的问题,并且指定了编码方式,所以同样的代码在不同的电脑上运行。

和思想:

  • new String(bytes, encoding)
  • string.getBytes(encoding)

如果您只使用流可以避免编码问题,这几行代码应该可以很好地完成工作

 public static String decompress(byte[] compressed) throws IOException {
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
            try (GZIPInputStream gis = new GZIPInputStream(
                    new ByteArrayInputStream(compressed))) {
                org.apache.commons.compress.utils.IOUtils.copy(gis, bos);
            }
            return bos.toString();
        }
    }