Docker容器(CentOS7)中文件字符集变为二进制问题求助
问题分析与解决方案
你的代码在Windows和物理机Linux上运行正常,但在CentOS7 Docker容器中出现文件以二进制格式写入的问题,大概率是外部Feed在Docker环境下返回了压缩后的chunked流,而你的代码没有处理解压逻辑,导致压缩的二进制字节被直接当作UTF-8字符写入文件,最终呈现为二进制格式。另外,当前代码用StringWriter中转的方式,对大流量Feed来说可能造成不必要的内存占用,也可以同步优化。
先验证问题根源
你可以在Docker容器里执行以下命令检查生成的文件:
- 用
file /tmp/feed.json查看文件类型,如果输出包含gzip compressed data,就说明流被压缩了; - 再用
zcat /tmp/feed.json尝试解压,如果能正常看到JSON内容,就坐实了这个问题。
为什么会出现环境差异?很多服务会根据请求头的Accept-Encoding决定是否返回压缩内容。可能你的应用在Docker容器中发送请求时,默认带上了Accept-Encoding: gzip, deflate,而Windows/物理Linux环境中没有,导致服务器返回的内容格式不同。
具体解决方案
方案1:修改请求头,强制获取未压缩流
在请求外部Feed时,设置Accept-Encoding: identity,让服务器返回原始未压缩的流:
// 假设你用HttpURLConnection发起请求 HttpURLConnection conn = (HttpURLConnection) new URL(feedUrl).openConnection(); // 添加请求头,禁止压缩 conn.setRequestProperty("Accept-Encoding", "identity"); InputStream in = conn.getInputStream();
方案2:自动识别并处理压缩流
如果无法修改请求头,或者服务强制返回压缩流,可以在代码中检测流是否被压缩,自动解压:
public void writeToFile(InputStream in){ File feedFile = new File("/tmp/feed.json"); // 用try-with-resources自动关闭流 try (InputStream processedIn = wrapCompressedStream(in); Writer fileWriter = new OutputStreamWriter(new FileOutputStream(feedFile, true), StandardCharsets.UTF_8)) { IOUtils.copy(processedIn, fileWriter); } catch (IOException e) { logger.error(Constants.FAILED_TO_WRITE_FEED_INTO_FILE,e); } } // 检测并包装压缩流 private InputStream wrapCompressedStream(InputStream in) throws IOException { PushbackInputStream pushbackIn = new PushbackInputStream(in, 2); byte[] headerBytes = new byte[2]; int readLen = pushbackIn.read(headerBytes); // 检查是否是gzip压缩的魔数 if (readLen == 2) { int magicNum = ((headerBytes[0] & 0xff) << 8) | (headerBytes[1] & 0xff); if (magicNum == GZIPInputStream.GZIP_MAGIC) { return new GZIPInputStream(pushbackIn); } } // 不是压缩流的话,把读出来的字节推回流中 if (readLen > 0) { pushbackIn.unread(headerBytes, 0, readLen); } return pushbackIn; }
方案3:优化文件写入逻辑(可选)
去掉没必要的FileUtils.touch,直接用字符流对接写入,避免StringWriter的内存中转,更适合大流量场景:
public void writeToFile(InputStream in){ File feedFile = new File("/tmp/feed.json"); try (InputStreamReader reader = new InputStreamReader(in, StandardCharsets.UTF_8); FileWriter writer = new FileWriter(feedFile, StandardCharsets.UTF_8, true)) { IOUtils.copy(reader, writer); } catch (IOException e) { logger.error(Constants.FAILED_TO_WRITE_FEED_INTO_FILE,e); } }
额外检查点
可以在Dockerfile中添加ENV LANG en_US.UTF-8,统一容器内的字符集环境。虽然你的代码已经指定了UTF-8编码,但统一环境可以避免某些工具查看文件时的显示异常。
内容的提问来源于stack exchange,提问作者Brajesh Pant
相关产品推荐
相关产品推荐

