通过CURL向RESTful PUT发送.gz文件触发ZipException问题求助
Hey,我之前也碰到过几乎一模一样的问题!单元测好好的,一用curl发请求就炸ZipException,排查下来确实是服务器把二进制GZIP数据当成UTF-8字符解析,把非UTF-8字节替换成了EF BF BD,直接破坏了GZIP的格式。结合Spring Boot + Jersey的特性,给你几个靠谱的解决思路:
核心原因
JAX-RS(Jersey)默认情况下,如果没有明确指定请求体的处理方式,可能会把输入流按字符流来解析——哪怕你参数用的是InputStream。这种字符编码转换(默认UTF-8)会把GZIP里的二进制字节搞坏,自然解压失败。
解决方案(按优先级尝试)
1. 给接口加@Consumes注解明确媒体类型
这是最直接的方法,告诉Jersey我们要接收的是application/gzip类型的二进制数据,别瞎搞字符编码:
public class Service { @PUT @Path("/{filename}") @Consumes("application/gzip") // 关键!指定接收二进制GZIP Response doPut(@Context HttpServletRequest request, @PathParam("filename") String filename, InputStream inputStream) { try { GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream); // 你的处理逻辑 } catch (IOException e) { e.printStackTrace(); return Response.serverError().entity("解压失败").build(); // 别返回null,返回合法响应 } return Response.ok().build(); } }
2. 换成byte[]作为请求体参数
有时候Jersey对byte[]的处理更“老实”,不会经过字符编码转换。试试把参数改成byte[],再转成流:
@PUT @Path("/{filename}") @Consumes("application/gzip") Response doPut(@Context HttpServletRequest request, @PathParam("filename") String filename, byte[] gzipBytes) { try { GZIPInputStream gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(gzipBytes)); // 处理逻辑 } catch (IOException e) { e.printStackTrace(); return Response.serverError().build(); } return Response.ok().build(); }
3. 配置Jersey强制二进制处理
如果上面两种方法都不行,就得从Jersey的全局配置入手,禁用自动字符编码转换。创建一个ResourceConfig的子类:
import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.server.ServerProperties; import org.springframework.context.annotation.Configuration; @Configuration public class JerseyConfig extends ResourceConfig { public JerseyConfig() { register(Service.class); // 禁用自动编码转换,强制按二进制处理请求体 property(ServerProperties.MEDIA_TYPE_MAPPINGS, "gzip:application/gzip"); // 确保Jersey不使用字符流读取请求 property(ServerProperties.RESPONSE_SET_STATUS_OVER_SEND_ERROR, true); } }
4. 排查Spring Boot的HttpMessageConverter干扰
如果你的项目同时用了Spring MVC和Jersey,Spring的HttpMessageConverter可能会抢着处理请求,把二进制转成字符。这时候可以试试排除Spring MVC的自动配置:
@SpringBootApplication(exclude = {WebMvcAutoConfiguration.class}) public class YourApplication { public static void main(String[] args) { SpringApplication.run(YourApplication.class, args); } }
5. 再确认curl命令的正确性
你的curl命令其实是对的,但再强调一下:一定要用--data-binary参数,别用-d或--data,因为后两者会按表单编码处理数据,可能偷偷改你的二进制:
curl -v -k -X PUT --user USER:Password -H "Content-Type: application/gzip" --data-binary @testFile.gz https://myapp.dev.com/testFile.gz
验证小技巧
修改完后,你可以用hexdump对比原始文件和服务器接收到的内容:
# 查看原始GZIP文件的前16字节(GZIP的魔法数是1F 8B) hexdump -n 16 testFile.gz
然后在服务器端打印输入流的前16字节,如果和上面的输出一致,说明数据没被损坏,解压肯定能正常运行。
内容的提问来源于stack exchange,提问作者JabberwokyStrollin

