Linux环境下Java解压Web服务Gzip响应失败求助
问题根源分析
你现在的代码犯了一个典型的二进制数据处理错误——把Gzip压缩的二进制响应先转换成字符串,再转回字节数组。字符串是专门用于表示文本的,而Gzip压缩包属于二进制数据,其中包含大量无法被字符编码正确映射的字节。当你把这些二进制字节转成字符串时,编码规则会自动替换掉无法识别的字节;再转回字节数组时,原始的Gzip结构已经被破坏,自然会导致GZIPInputStream抛出「Not in Gzip format」错误。
为什么Windows下看似正常?这只是巧合:Windows默认的windows-1252是单字节编码,刚好Gzip开头的标识字节(0x1F和0x8B)在这个编码下能被正确映射,转字符串再转回字节数组时没被篡改;但Linux默认的UTF-8是多字节编码,当二进制字节无法对应有效UTF-8字符时,会被替换成0xFFFD(替换字符),直接破坏了Gzip的标识头,所以错误立刻显现。
解决方案
核心思路:直接操作二进制流,完全绕过字符串中转。下面是具体的代码修正步骤和优化后的完整代码:
关键修正方向
- 移除字符串中转逻辑:不再用字符流(
BufferedReader)读取二进制响应,改用字节流直接处理Gzip数据 - 重构解压方法:让
unzip直接接收InputStream,避免不必要的字节数组转换,同时减少内存占用 - 明确编码规则:所有涉及字符编码的场景都指定
UTF-8,彻底摆脱系统默认编码的影响 - 统一响应处理:成功和错误响应的流处理逻辑保持一致,避免重复代码
修正后的完整代码
import java.io.*; import java.net.*; import java.nio.charset.StandardCharsets; import java.util.zip.GZIPInputStream; public class WSConnectTest { public final static String UserName = null; // User id login for Fusion public final static String instanceURL = null; public final static String USER_PWD = null; // API key shared by CSOD private static final String PROXY_URL = null; // UBS proxy URL private static final int PROXY_PORT = 8080; private static final String PROXY_USERNAME = "USER_NAME"; private static final String PROXY_PASSWORD = "PASSWORD"; final static String USER_AGENT = "Mozilla/5.0"; static Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(PROXY_URL, PROXY_PORT)); static { Authenticator authenticator = new Authenticator() { public PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(UserName, USER_PWD.toCharArray()); } }; Authenticator.setDefault(authenticator); } public static void main(String[] args) throws Exception { String theURL = instanceURL + "<RESOURCE_NAME>"; System.out.println("The URL to be called is : " + theURL); String json = "<JSON_STRING>"; System.out.println("The json is :" + json); PostRequestWithFilter(theURL, json); } private static void PostRequestWithFilter(String url, String json) throws Exception { HttpURLConnection con = null; try { URL obj = new URL(url); con = (HttpURLConnection) obj.openConnection(proxy); con.setRequestMethod("POST"); con.setRequestProperty("User-Agent", "Apache-HttpClient/4.1.1 (java 1.5)"); con.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); con.setRequestProperty("Accept-Language", "UTF-8"); con.setRequestProperty("Accept-Encoding", "gzip, deflate"); con.setDoOutput(true); con.setConnectTimeout(15000); System.out.println("Request Properties :" + con.getRequestProperties()); // 发送请求体:明确用UTF-8编码,避免系统默认编码干扰 try (OutputStream os = con.getOutputStream()) { byte[] input = json.getBytes(StandardCharsets.UTF_8); os.write(input, 0, input.length); } int responseCode = con.getResponseCode(); System.out.println("\nSending 'POST' request to URL : " + url); System.out.println("\nResponse Code : " + responseCode); System.out.println("\nResponse message : " + con.getResponseMessage()); System.out.println("Response Content Encoding :" + con.getContentEncoding()); // 统一获取响应流(成功用getInputStream,失败用getErrorStream) InputStream responseStream = responseCode == HttpURLConnection.HTTP_CREATED ? con.getInputStream() : con.getErrorStream(); // 根据响应头判断是否需要解压 String contentEncoding = con.getContentEncoding(); if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip")) { String decompressedResponse = unzip(responseStream); System.out.println("Decompressed response :" + decompressedResponse); } else { // 非压缩响应直接读取文本 try (BufferedReader reader = new BufferedReader( new InputStreamReader(responseStream, StandardCharsets.UTF_8))) { StringBuilder response = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { response.append(line); } System.out.println("Response :" + response.toString()); } } } catch (Exception e) { e.printStackTrace(); } finally { if (con != null) { con.disconnect(); } } } // 直接从InputStream解压Gzip内容,避免字节数组中转 public static String unzip(InputStream inputStream) throws IOException { StringBuilder output = new StringBuilder(); try (GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream); BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(gzipInputStream, StandardCharsets.UTF_8))) { String line; while ((line = bufferedReader.readLine()) != null) { output.append(line); } } return output.toString(); } }
额外优化说明
- 利用try-with-resources:自动关闭流资源,避免手动关闭可能导致的资源泄漏
- 依赖响应头判断压缩类型:比起自己检查字节头,
con.getContentEncoding()更符合HTTP协议规范,也更可靠 - 明确请求体编码:在
Content-Type头里指定charset=UTF-8,同时用StandardCharsets.UTF_8转字节数组,确保请求体在跨平台环境下不会乱码
内容的提问来源于stack exchange,提问作者NiranjanD
相关产品推荐
相关产品推荐

