要解决这个问题,我们需要明确两个关键点:读取/创建zip文件时正确指定输入输出流的charset以及妥善处理ZipEntry中涉及到字符串的部分。
首先,在构造 ` ZipOutputStream ` 或者 ` ZipInputStream ` 时,并不能直接设置其内部使用的 charset ,但可以通过包装字节流的方式来间接实现:
java
// 创建一个支持GBK编码的 FileOutputStream 和 BufferedOutputStream
FileOutputStream fos = new FileOutputStream("archive.zip");
BufferedOutputStream bos = new BufferedOutputStream(fos);
Charset setEncodingToGb2312 = Charset.forName("GB2312"); // 根据实际需要选择合适的编码方式
// 使用转换流将原生IO流转为自定义编码的输出流
OutputStream out = new OutputStreamWriter(bos, setEncodingToGb2312).getOutputStream();
// 构造并配置 ZipOutputStream
ZipOutputStream zos = new ZipOutputStream(out);
...
zos.putNextEntry(new ZipEntry(file.getName().getBytes(setEncodingToGb2312.name())));
...
上述代码片段展示了如何以 GBK 编码的方式新建 ZIP 输出流并对含有中文路径名的文件添加到 zip 归档里去的过程。对于解压过程也是类似的逻辑,需确保从 ZipInputStream 中获取出的 entry 名称用相同的编码方式进行还原:
java
ZipInputStream zis = new ZipInputStream(new FileInputStream("archive.zip"));
zis.getNextEntry();
byte[] encodedBytes = zis.getCurrentEntryName().getBytes(Charset.forName("ISO-8859-1")); // 先按PKWare规范拿到原始Byte数组
String decodedFileName = new String(encodedBytes, "GB2312"); // 再按照正确的文本编码解析成字符串
然而更推荐的做法是遵循 PKWARE 的 Unicode 路径扩展规范 (APPENDIX D of APPNOTE.TXT),该规范允许存储 UTF-8 编码的文件名且不需要额外设定:
java
try(ZipOutputStream zout = new ZipOutputStream(Files.newOutputStream(zipPath))) {
for(Path file : filesToAdd) {
Path absoluteFilePath = file.toAbsolutePath();
ZipEntry ze= new ZipEntry(absoluteFilePath.toString());
ze.setComment(String.format("%s created on %tc", absoluteFilePath.getFileName(), System.currentTimeMillis()));
// 设置名字编码方式为 utf-8 并保存进 extra 字段
byte[] fileNameInUtf8 = Files.readAllLines(absoluteFilePath.getParent()).toArray()[0].getBytes(StandardCharsets.UTF_8);
ze.setName(fileNameInUtf8);
ze.setSize(Files.size(absoluteFilePath));
zout.putNextEntry(ze);
Files.copy(absoluteFilePath, zout);
zout.closeEntry();
}
} catch(...) {...}
// 解压时无需特殊处理即可正常显示中文名
通过这种方式构建和解读Zip条目可以避免大部分由于编码不一致造成的中文文件名混乱的问题。同时注意无论是压缩还是解压的过程中,都要尽可能保持两端系统环境、程序设计所采用的字符集的一致性才能有效防止乱码情况的发生。