【问题标题】:Error: "java.util.zip.ZipException: Not in GZIP format" when interpreting gzip response body from API解释来自 API 的 gzip 响应正文时出现错误:“java.util.zip.ZipException:不是 GZIP 格式”
【发布时间】:2021-08-23 13:11:46
【问题描述】:

目前,我正在尝试对基于 mediawiki 的 API 进行简单的 get 调用,特别是 liquipedia.net。在他们的使用条款中,他们要求接受 gzip 编码,并且在打印响应标头和状态代码后,我可以判断我的调用是否成功。但是,响应正文会打印出我不知道如何解释的难以理解的字符模糊。我试图整合一些解压信息的方法,因为我假设我收到的信息可能仍然在 .GZ 格式中,但是我使用的方法通常会提供错误,表明数据不在 gzip 格式中或什么都没有已经完成了任何信息。

这是建立连接并尝试简单地打印响应信息的基本方法。

public static void GET(String address) throws IOException, InterruptedException
{
    HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create(address))
    .header("Accept-Encoding", "Gzip")
    .header("User-Agent", "RosterBot")
    .GET()
    .build();

    HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
    HttpHeaders responseHeader = response.headers();

    
    System.out.println(responseHeader.toString());
    System.out.println(response.statusCode());



    System.out.println(decompress(response.body().getBytes()));
    
}

这是我使用的一种压缩方法,并且看到其他一些方法使用

public static byte[] compress(final String str) throws IOException {
  
    System.out.println(b.toString());
    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 "";
    }
     
      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);
      }
    
    return outStr.toString();
  }

这是我经常获得的响应主体的示例

%�1���lM!��^aag,/J�p��w w��^d{p-����f 4��}������Z

以及我收到的响应标头

{ {:status=[200], accept-ranges=[bytes], age=[0], cache-control=[private, must-revalidate, max-age=0], content-disposition=[inline ;文件名=api-result.json],内容编码=[gzip],内容类型=[应用程序/json; charset=utf-8], date=[Sat, 05 Jun 2021 23:56:40 GMT], referrer-policy=[no-referrer-when-downgrade], server=[nginx], vary=[Accept-Encoding,视为不受信任,Cookie],via=[1.1 varnish (Varnish/6.5)],x-cache=[MISS],x-content-type-options=[nosniff],x-frame-options=[DENY] , x-xss-保护=[1;模式=块]} }

我想我可能遗漏了一些明显的东西,但由于我对使用 API 有点陌生,如果能提供任何帮助,我将不胜感激!

【问题讨论】:

  • 那不是解压方式。那只是两段不同的代码卡在一起,甚至无法编译。没有任何减压的尝试。只需将GZIPInputStream 包装在响应的输入流中即可。
  • 我很抱歉,似乎完整的方法没有复制到代码 sn-p。我刚刚更新了它以反映完整的方法。
  • 嗯,这也不能编译,有一个没有ifelse,但如果它这样做肯定会正确解压缩这个响应的Content-encoding: gzip 并给你返回它声明的JSON包含装有。那么你的问题是什么?
  • 问题在于它并没有做到这一切,它只是提供了与我上面提供的相同的乱码字符,或者它提供了非信息,如“[B@307f6b8c”,我知道由于 API 文档中提供的示例,这不是正确的响应。或者,它只是提供一个错误,指出它“不是 GZIP 格式”。
  • 不是,问题是你贴的代码没有编译通过,但是解压代码永远无法提供[B@307f6b8c,这是调用byte[].toString()的结果。你声称的行为是不可能的。发布真实代码:特别是发布打印响应的代码。

标签: java api encoding httpclient gzip


【解决方案1】:

我通过像这样修改我的 HttpResponse 的 T(类型)解决了这个问题

HttpResponse<InputStream> response = client.send(request, BodyHandlers.ofInputStream());

之后,我用 GZIPInputStream 解析数据并输出信息

InputStreamReader reader = new InputStreamReader(new GZIPInputStream(response.body()));

    while (true) {
      int ch = reader.read();
      if (ch==-1) {
         break;
      }
      System.out.print((char)ch);
    }

【讨论】:

  • 这是一个很好的解决方案。原始代码中的错误是将原始字节转换为字符串并返回原始字节 - 因为这通常不是无损转换,除非字节确实代表字符串并且您知道要使用哪个字符集。如果您使用 ofByteArray() 代替它会起作用 - 但当然,以在内存中缓冲整个 gzip 的内容为代价。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-13
  • 2011-06-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多