从扩展名确定文件类型
Determining File Type from Extension
Java 中是否有一种简单的方法可以将文件扩展名转换为特定文件类型?也就是说,我想将“.doc”翻译成“Microsoft Word 文档”。但我也不想查看每个文件内部以确定 MIME 类型等,因为这会影响性能。
是否有库或数据库文件列出所有当前接受的扩展名及其含义?我可以以编程方式加载某些内容,然后在需要时进行搜索吗?
Microsoft has a support article 常见文件扩展名。
“list of file formats”上的维基百科页面似乎非常详尽。
应该很容易复制/粘贴其中一个(或两个)列表,使用文本编辑器调整格式,并通过硬编码数组或外部资源文件将其推送到您的代码中。
请记住,如果您走这条路,那么您就相信这些文件包含扩展程序声明的数据类型。这将是一个有点脆弱的解决方案,因为它只需要一个上游错误就可以解决问题。
IMO 更好的方法是从文件本身的前几个字节中读取 magic numbers(即文件签名)。几乎任何生产或商业软件都会这样做(至少)而不是信任文件扩展名。恶意用户仍有可能伪造签名,但这需要有意的操作,而不仅仅是编码错误。
这样做的成本与检查扩展的成本确实没有太大区别;除非你正在处理大量的文件或者有一些非常紧迫的截止日期(在这种情况下,Java 可能不是最好的选择),这两种方法都不需要太多要求 OS 阅读来自磁盘的少数字节。检查幻数只需要读取更多的字节,以及每个文件上 opening/closing 流的开销。
据我所知,这些名称只能在注册表中找到。我见过尝试使用 Swing 的 FileView 来获取它们,但我不会依赖它,尤其是对于无头代码。
public static String getFullFileTypeName(String extension)
throws IOException,
InterruptedException {
if (!extension.startsWith(".")) {
extension = "." + extension;
}
String progID = readDefaultValue(extension);
String name = readDefaultValue(progID);
return name;
}
private static String readDefaultValue(String node)
throws IOException,
InterruptedException {
String registryPath = "HKEY_CLASSES_ROOT\" + node;
ProcessBuilder builder = new ProcessBuilder(
"reg.exe", "query", registryPath, "/ve");
builder.redirectError(ProcessBuilder.Redirect.INHERIT);
String value = null;
Process process = builder.start();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
int regSZIndex = line.indexOf("REG_SZ");
if (regSZIndex >= 0) {
value = line.substring(regSZIndex + 6).trim();
break;
}
}
// Consume remaining output.
if (line != null) {
int c;
do {
c = reader.read();
} while (c >= 0);
// As of Java 11, the above loop can be replaced with:
//reader.transferTo(Writer.nullWriter());
}
}
int returnCode = process.waitFor();
if (returnCode != 0) {
throw new IOException("Got return code " + returnCode
+ " from " + builder.command());
}
if (value == null) {
throw new IOException(
"Could not find value for \"" + registryPath + "\"");
}
return value;
}
Java 中是否有一种简单的方法可以将文件扩展名转换为特定文件类型?也就是说,我想将“.doc”翻译成“Microsoft Word 文档”。但我也不想查看每个文件内部以确定 MIME 类型等,因为这会影响性能。
是否有库或数据库文件列出所有当前接受的扩展名及其含义?我可以以编程方式加载某些内容,然后在需要时进行搜索吗?
Microsoft has a support article 常见文件扩展名。
“list of file formats”上的维基百科页面似乎非常详尽。
应该很容易复制/粘贴其中一个(或两个)列表,使用文本编辑器调整格式,并通过硬编码数组或外部资源文件将其推送到您的代码中。
请记住,如果您走这条路,那么您就相信这些文件包含扩展程序声明的数据类型。这将是一个有点脆弱的解决方案,因为它只需要一个上游错误就可以解决问题。
IMO 更好的方法是从文件本身的前几个字节中读取 magic numbers(即文件签名)。几乎任何生产或商业软件都会这样做(至少)而不是信任文件扩展名。恶意用户仍有可能伪造签名,但这需要有意的操作,而不仅仅是编码错误。
这样做的成本与检查扩展的成本确实没有太大区别;除非你正在处理大量的文件或者有一些非常紧迫的截止日期(在这种情况下,Java 可能不是最好的选择),这两种方法都不需要太多要求 OS 阅读来自磁盘的少数字节。检查幻数只需要读取更多的字节,以及每个文件上 opening/closing 流的开销。
据我所知,这些名称只能在注册表中找到。我见过尝试使用 Swing 的 FileView 来获取它们,但我不会依赖它,尤其是对于无头代码。
public static String getFullFileTypeName(String extension)
throws IOException,
InterruptedException {
if (!extension.startsWith(".")) {
extension = "." + extension;
}
String progID = readDefaultValue(extension);
String name = readDefaultValue(progID);
return name;
}
private static String readDefaultValue(String node)
throws IOException,
InterruptedException {
String registryPath = "HKEY_CLASSES_ROOT\" + node;
ProcessBuilder builder = new ProcessBuilder(
"reg.exe", "query", registryPath, "/ve");
builder.redirectError(ProcessBuilder.Redirect.INHERIT);
String value = null;
Process process = builder.start();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
int regSZIndex = line.indexOf("REG_SZ");
if (regSZIndex >= 0) {
value = line.substring(regSZIndex + 6).trim();
break;
}
}
// Consume remaining output.
if (line != null) {
int c;
do {
c = reader.read();
} while (c >= 0);
// As of Java 11, the above loop can be replaced with:
//reader.transferTo(Writer.nullWriter());
}
}
int returnCode = process.waitFor();
if (returnCode != 0) {
throw new IOException("Got return code " + returnCode
+ " from " + builder.command());
}
if (value == null) {
throw new IOException(
"Could not find value for \"" + registryPath + "\"");
}
return value;
}