DocFetcher 1.1.5 试用及 zip 文件中文问题解决
发表于 : 2013-02-06 1:58
转自 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:
而日常我们使用的 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 中:
从上面这段代码可见, 系统默认得到的 ZipDriver 使用的是默认字符集 IBM437, 所以, 会产生中文问题;
解决这个问题的方式是对 ZipDriverService 进行一定的调整, 以便通过环境变量或者 Java 系统属性来调整 ZipDriver 的默认字符集, 然后把这个 class 以 jar 补丁的形式, 放到 CLASSPATH 的最前面. 修改后的 ZipDriverService 代码如下:
具体编译后的补丁包, 以及如何在启动脚本中设置 zip 文件名的编码, 可以到 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);
}
... ...
默认情况下, 通过 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;
}
}
解决这个问题的方式是对 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;
}
}