通过CURL将.gz文件发送到RESTful,在GZIPInputStream中创建ZipException

时间:2018-05-31 21:15:08

标签: spring-boot jersey kubernetes jax-rs gzip

我正在创建的应用程序将一个gzip压缩文件发送到RESTful PUT,解压缩该文件然后进行进一步处理,如下所示:

public class Service {

  @PUT
  @Path("/{filename}")
  Response doPut(@Context HttpServletRequest request,
      @PathParam("filename") String filename,
      InputStream inputStream) {

      try {
        GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream);

        // Do Stuff with GZIPInputStream
      } catch (IOException e) {
        e.printStackTrace();
      }
      return null;
  }
}

我能够在单元测试中成功发送一个gzip压缩文件,如下所示:

InputStream inputStream = new FileInputStream("src/main/resources/testFile.gz);
Service service = new Service();
service.doPut(mockHttpServletRequest, "testFile.gz", inputStream);
// Verify processing stuff happens

但是当我构建应用程序并尝试使用以下内容从src / main / resources目录中CURL相同的文件时,我得到一个ZipException:

curl -v -k -X PUT --user USER:Password -H "Content-Type: application/gzip" --data-binary @testFile.gz https://myapp.dev.com/testFile.gz

例外是:

java.util.zip.ZipException: Not in GZIP format
    at java.util.zip.GZIPInputStream.readHeader(GZIPInputStream.java:165)
    at java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:79)
    at java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:91)
    at Service.doPut(Service.java:23)
    // etc.

那么有没有人知道为什么通过CURL发送文件会导致ZipException?

更新: 我最后看一下通过InputStream发送的实际字节,并找出ZipException:GZIP格式错误来自哪里。 GZIP文件的前两个字节分别需要为1F和8B,以便GZIPInputStream将数据识别为GZIP格式。相反,8B字节以及蒸汽中不与有效UTF-8字符相对应的每个其他字节被转换为字节EF,BF,BD,它们是UTF-8未知字符替换字节。因此,服务器将GZIP数据读取为UTF-8字符而不是二进制数据,并且正在破坏数据。

我现在遇到的问题是我无法弄清楚我需要更改配置的位置,以便让服务器将压缩数据视为二进制与UTF-8。该应用程序使用Spring-Boot在Jersey服务器上使用Jax-rs,该服务器部署在Kubernetes pod中并作为服务运行,因此需要调整其中一种技术的设置以防止在网络上使用不正确的编码。数据

我尝试添加-H&#34; Content-Encoding:gzip&#34;到curl命令,在jersey ResourceConfig类中注册EncodingFilter.class和GZipEncoder.class,将application / gzip添加到application.propertes中的server.compression.mime-types,添加@Consumes(&#34; application / gzip&# 34;)上面的doPut方法的注释,以及其他一些我无法记住的事情,但似乎没有任何效果。

我在详细的CURL日志中看到以下内容:

> PUT /src/main/resources/testFile.gz
> HOST: my.host.com
> Authorization: Basic <authorization stuff>
> User-Agent: curl/7.54.1
> Accept: */*
> Content-Encoding: gzip
> Content-Type: application/gzip
> Content-Length: 31
>
} [31 bytes data]
* upload completely sent off: 31 out of 31 bytes
< HTTP/1.1 500
< X-Application-Context: application
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: <date stuff>
...etc

我所做的一切都没有影响接收方

Content-Type: application/json;charset=UTF-8

部分,我怀疑是这个问题。

1 个答案:

答案 0 :(得分:0)

我遇到了同样的问题,最后使用-H 'Content-Type:application/json;charset=UTF-8'

解决了

使用Charles查找差异

我可以使用邮递员成功发送压缩后的文件。所以我用查尔斯抓了两个分别由curl和postman发送的包裹。比较这两个程序包后,我发现Postman使用application/json作为内容类型,而curl使用text/plain

Spring docs: Content Type and Transformation

根据Spring文档,如果内容类型为text/plain并且源有效载荷为byte[],则Spring将使用内容类型标头中指定的字符集将有效载荷转换为字符串。这就是ZipException发生的原因。由于原始字节数据已经被解码,不再采用gzip格式。

Spring source code

@Override
protected Object convertFromInternal(Message<?> message, Class<?> targetClass, @Nullable Object conversionHint) {
    Charset charset = getContentTypeCharset(getMimeType(message.getHeaders()));
    Object payload = message.getPayload();
    return (payload instanceof String ? payload : new String((byte[]) payload, charset));
}