分页: 1 / 1

DocFetcher 1.1.5 试用及 zip 文件中文问题解决

发表于 : 2013-02-06 1:58
glassprogrammer
转自 http://www.thinkbase.net/main/blog/thinkbase-2012/12/09
----
自从接触 ​DocFetcher 以来, 就一直使用它来搜索自己的各类文档, 当时也总结过这个软件存在的问题, 详见 桌面搜索工具 DocFetcher 试用笔记(http://www.thinkbase.net/main/blog/thin ... 12/12/02-2);

最近发现 ​DocFetcher 已经发布了 1.1.5 版本, 根据网站上的介绍, 1.1 版本经过完全重写(rewritten from scratch), 增加了大量的新特征(详见 ​http://docfetcher.sourceforge.net/wiki/ ... es_in_v1.1), 包括可以支持搜索压缩包中的内容, 而且支持嵌套压缩;

经过简单试用, 觉得这个版本还是值得升级的, 随后发现在搜索 zip 格式压缩包里面的内容时, 中文文件名会变成乱码. 具体现象如下图所示, 虽然可以索引压缩文件中的内容, 但是 zip 格式压缩包中中文文件名显示为乱码, 而 7z 和 rar 格式则显示正确;

下载代码(git clone http://git.code.sf.net/p/docfetcher/code docfetcher-code)研究了一下, 发现 ​DocFetcher 使用了 ​truezip 来进行 zip 格式压缩文件的搜索(支持 jar|tar|tar.bz2|tar.gz|tb2|tbz|tgz|zip 等多种格式), 而在 ​truezip 中, zip 格式默认使用的字符集为 IBM437:

代码: 全选

... ...
public class ZipDriver
extends FsCharsetArchiveDriver<ZipDriverEntry>
implements ZipOutputStreamParameters, ZipFileParameters<ZipDriverEntry> {

    private static final Logger logger = Logger.getLogger(
            ZipDriver.class.getName(),
            ZipDriver.class.getName());

    /**
     * The character set for entry names and comments in "traditional"
     * ZIP files, which is {@code "IBM437"}.
     */
    private static final Charset ZIP_CHARSET = Charset.forName("IBM437");

    private final IOPool<?> ioPool;

    /**
     * Constructs a new ZIP driver.
     * This constructor uses {@link #ZIP_CHARSET} for encoding entry names
     * and comments.
     *
     * @param ioPoolProvider the provider for I/O entry pools for allocating
     *        temporary I/O entries (buffers).
     */
    public ZipDriver(IOPoolProvider ioPoolProvider) {
        this(ioPoolProvider, ZIP_CHARSET);
    }
    ... ...
而日常我们使用的 zip 压缩文件都是使用本地字符集进行压缩的(在中文环境下, 一般就是GBK), 继续研究发现, ​DocFetcher 使用 ​truezip 的 TFile, 实现与 java.io.File 相似的方式统一访问文件系统目录和压缩文件(也就是把压缩文件看作一个目录), 而 TFile 内部则通过一个 TArchiveDetector 类型的成员变量来依据后缀名确定使用那种 "Driver" 操作具体的压缩文件;

默认情况下, 通过 TConfig.getArchiveDetector() 得到的 ArchiveDetector 是 TArchiveDetector.ALL, 此时在 TArchiveDetector 中实际使用 FsDriverLocator.SINGLETON 来获得所有加载的 "Driver", FsDriverLocator 会通过 ServiceLocator 查找并加载所有可用的 Driver, zip 格式相关 Driver 的定义实现在 ZipDriverService 中:

代码: 全选

@Immutable
public final class ZipDriverService extends FsDriverService {

    private static final Map<FsScheme, FsDriver>
            DRIVERS = newMap(new Object[][] {
                { "zip", new ZipDriver(IOPoolLocator.SINGLETON) },
                { "ear|jar|war", new JarDriver(IOPoolLocator.SINGLETON) },
                { "odt|ott|odg|otg|odp|otp|ods|ots|odc|otc|odi|oti|odf|otf|odm|oth|odb", new OdfDriver(IOPoolLocator.SINGLETON) },
                { "exe", new ReadOnlySfxDriver(IOPoolLocator.SINGLETON) },
            });

    @Override
    public Map<FsScheme, FsDriver> get() {
        return DRIVERS;
    }
}
从上面这段代码可见, 系统默认得到的 ZipDriver 使用的是默认字符集 IBM437, 所以, 会产生中文问题;

解决这个问题的方式是对 ZipDriverService 进行一定的调整, 以便通过环境变量或者 Java 系统属性来调整 ZipDriver 的默认字符集, 然后把这个 class 以 jar 补丁的形式, 放到 CLASSPATH 的最前面. 修改后的 ZipDriverService 代码如下:

代码: 全选

@Immutable
public final class ZipDriverService extends FsDriverService {
        private static final String SYS_PROP_ZIP_CHARSET = ZipDriverService.class.getPackage().getName() + ".ZIP_CHARSET";
        private static final String ENV_VAR_ZIP_CHARSET = "TRUEZIP_ZIP_CHARSET";

        private static final ZipDriver buildZipDriver(){
                String charset = System.getProperty(SYS_PROP_ZIP_CHARSET);
                if (null!=charset && charset.trim().length() > 0){
                        return new ZipDriver(
                                        IOPoolLocator.SINGLETON, Charset.forName(charset.trim()));
                }
                charset = System.getenv(ENV_VAR_ZIP_CHARSET);
                if (null!=charset && charset.trim().length() > 0){
                        return new ZipDriver(
                                        IOPoolLocator.SINGLETON, Charset.forName(charset.trim()));
                }
                
                return new ZipDriver(IOPoolLocator.SINGLETON);
        }

    private static final Map<FsScheme, FsDriver>
            DRIVERS = newMap(new Object[][] {
                { "zip", buildZipDriver() },
                { "ear|jar|war", new JarDriver(IOPoolLocator.SINGLETON) },
                { "odt|ott|odg|otg|odp|otp|ods|ots|odc|otc|odi|oti|odf|otf|odm|oth|odb", new OdfDriver(IOPoolLocator.SINGLETON) },
                { "exe", new ReadOnlySfxDriver(IOPoolLocator.SINGLETON) },
            });

    @Override
    public Map<FsScheme, FsDriver> get() {
        return DRIVERS;
    }
}
具体编译后的补丁包, 以及如何在启动脚本中设置 zip 文件名的编码, 可以到 http://www.thinkbase.net/main/blog/thinkbase-2012/12/09 查看.