Zip字符集问题

问题

Zip的使用场景是,将workDir、数据data进行压缩,然后传输到oss指定的位置上。

这里出现的问题是,汉字文件名,zip出现乱码。英文文件名没问题,文件中的内容出现的汉字也没问题。

原因

这个原因肯定是字符集的问题,找到的全部中文都是建议使用org.apache.tools.zip来替换java.util.zip,然后使用前者来指定编码的字符集,比如linux乱码问题解决或者这篇

正常的情况下,只要Zip的encode与decode使用相同的字符集,就不会出现乱码问题。从代码来看,Zip默认使用的就是StandardCharsets.UTF_8(标准UTF_8),而unzip默认使用的就是UTF_8的解密方式。现在出现了问题,那肯定是encode、或者decode的UTF_8不匹配。

找了半天,在一个国外的论坛上有这样的解释,如下:

The java.util.zip.* stuff is hardwired with the assumption that filenames in
ZIP files (and probably comments too, but I haven't checked) are encoded in the
weird non-standard, incompatible, modified version of UTF8 that is used
internally by some low-level JVM stuff (principally the JNI interface, and JVM
classfile format) ! Nobody else on Earth uses it for anything.

这里说的挺明白了Zip file的文件名使用的是非标准、不兼容的UTF8修正版,只在底层的JVM中使用,其他地方都没有使用。

这样就验证了我们的猜测,原因在于java.util.zip使用的utf8不是标准的utf8问题。

方案

既然问题找到了,那方案就迎刃而解了,我们只要重新指定一个字符集即可,这里指定了Hutool的GBK编码,unzip解码时,需要指定字符集。unzip -O CP936 xxx.zip

 // Create zip path
        Path markdownZipPath =  createFilePath(haloProperties.getBackupMarkdownDir(), HALO_BACKUP_MARKDOWN_PREFIX, ".zip");

        // Zip file
        try (ZipOutputStream markdownZipOut = new ZipOutputStream(
            Files.newOutputStream(markdownZipPath), CharsetUtil.CHARSET_GBK)) {
            // Zip temporary directory
            Path markdownFileTempPath = Paths.get(markdownFileTempPathName);
            run.halo.app.utils.FileUtils.zip(markdownFileTempPath, markdownZipOut);

            // Zip upload sub-directory
            String uploadPathName =
                FileHandler.normalizeDirectory(haloProperties.getWorkDir()) + UPLOAD_SUB_DIR;
            Path uploadPath = Paths.get(uploadPathName);
            if (Files.exists(uploadPath)) {
                run.halo.app.utils.FileUtils.zip(uploadPath, markdownZipOut);
            }

            // Remove files in the temporary directory
            run.halo.app.utils.FileUtils.deleteFolder(markdownFileTempPath);

            // Build backup dto
            return buildBackupDto(DATA_EXPORT_MARKDOWN_BASE_URI, markdownZipPath);
        } catch (IOException e) {
            throw new ServiceException("Failed to export markdowns", e);
        }

其他

对于以上代码,再多说一点 关于try()。无意中将try()中的代码写到了try代码块中,发现最后的zip格式错误,且为空。原因就在于try(),这种叫做:try-with-resources语句。try-with-resources 语句是一种声明一个或多个资源的 try 语句。资源是程序完成后必须关闭的对象。 try-with-resources 语句确保每个资源在语句结束时关闭。

如果写到try代码块中,需要手动关闭resource。

# 语言 

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×