将非ASCII文件名添加到Java中的zip中

时间:2020-03-06 14:28:13  来源:igfitidea点击:

使用Java将非ASCII文件名添加到zip文件中的最佳方法是什么,以便可以在Windows和Linux中正确读取这些文件?

这是一种尝试,改编自https://truezip.dev.java.net/tutorial-6.html#Example,可在Windows Vista中使用,但在Ubuntu Hardy中无法使用。在Hardy中,文件名在file-roller中显示为abc-.txt。

import java.io.IOException;
import java.io.PrintStream;

import de.schlichtherle.io.File;
import de.schlichtherle.io.FileOutputStream;

public class Main {

    public static void main(final String[] args) throws IOException {

        try {
            PrintStream ps = new PrintStream(new FileOutputStream(
                    "outer.zip/abc-???.txt"));
            try {
                ps.println("The characters ??? works here though.");
            } finally {
                ps.close();
            }
        } finally {
            File.umount();
        }
    }
}

与java.util.zip不同,truezip允许指定zip文件编码。这是另一个示例,这次明确指定了编码。 IBM437,UTF-8和ISO-8859-1在Linux中均不起作用。 IBM437在Windows中工作。

import java.io.IOException;

import de.schlichtherle.io.FileOutputStream;
import de.schlichtherle.util.zip.ZipEntry;
import de.schlichtherle.util.zip.ZipOutputStream;

public class Main {

    public static void main(final String[] args) throws IOException {

        for (String encoding : new String[] { "IBM437", "UTF-8", "ISO-8859-1" }) {
            ZipOutputStream zipOutput = new ZipOutputStream(
                    new FileOutputStream(encoding + "-example.zip"), encoding);
            ZipEntry entry = new ZipEntry("abc-???.txt");
            zipOutput.putNextEntry(entry);
            zipOutput.closeEntry();
            zipOutput.close();
        }
    }
}

解决方案

它真的失败了还是仅仅是字体问题? (例如,那些字符的字体具有不同的字形)在Windows中,由于字体不支持字符集,但数据实际上是完整无误的,因此在渲染"中断"时出现了类似的问题。

非ASCII文件名在ZIP实施中不可靠,最好避免使用。没有提供将字符集设置存储在ZIP文件中的规定;客户倾向于猜测"当前系统代码页",这不太可能是我们想要的。客户端和代码页的许多组合都可能导致文件无法访问。

对不起!

通过快速浏览TrueZIP手册,他们推荐了JAR格式:

It uses UTF-8 for file name encoding
  and comments - unlike ZIP, which only
  uses IBM437.

这可能意味着该API使用java.util.zip包进行了实现;该文档指出,从1996年开始,它仍在使用ZIP格式。直到2006年,才对PKWARE .ZIP文件格式规范添加了Unicode支持。

ZIP中File-Entries的编码最初指定为IBM Code Page437. 其他语言中使用的许多字符不可能以这种方式使用。

PKWARE规范提到了这个问题,并增加了一点。但这是后来添加的内容(从2007年开始,感谢Cheeso清除了该内容,请参阅评论)。如果该位置1,则文件名条目必须以UTF-8编码。此扩展在链接文档末尾的"附录D语言编码(EFS)"中进行了描述。

对于Java,使用非ASCII字符会遇到麻烦是一个已知的错误。请参阅错误#4244499和大量相关错误。

我的同事在将文件名存储到ZIP中并在读取文件后进行解码之前,将其用作URL编码的变通方法。如果同时控制存储和读取,则可能是一种解决方法。

编辑:在错误处,有人建议使用Apache Ant中的ZipOutputStream作为解决方法。此实现允许指定编码。

根据PKWare拥有的规范,在Zip文件中,文件名和文件注释的编码为IBM437. 在2007年,PKWare扩展了规范,使其还允许UTF-8. 这没有说明zip中包含的文件的编码。仅文件名的编码。

我认为所有工具和库(Java和非Java)都支持IBM437(这是ASCII的超集),并且更少的工具和库支持UTF-8. 一些工具和库支持其他代码页。例如,如果我们在上海运行的计算机上使用WinRar压缩某些内容,则将获得Big5代码页。 zip规范未"允许"此操作,但还是会发生这种情况。

.NET的DotNetZip库支持Unicode,但是如果我们使用Java,那当然对我们没有帮助!

使用Java内置的ZIP支持,我们将始终获得IBM437. 如果要使用非IBM437的档案进行归档,请使用第三方库或者创建一个JAR。

确实发生了奇迹,Sun / Oracle确实修复了长期存在的错误/ rfe:

现在,可以在创建zip文件/流时设置文件名编码(需要Java 7)。

我们仍然可以使用zip流的Apache Commons实现:http://commons.apache.org/compress/apidocs/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.html#setEncoding%28java.lang.String %29

在流上调用setEncoding(" UTF-8")应该足够了。