如何在 Ubuntu 中获取文件的图标和类型描述?

How to get file's icon and type description in Ubuntu?

在 Windows 上使用 Java 11,我可以使用以下方法获取有关我的文件的信息:

import javax.swing.filechooser.FileSystemView

var type = FileSystemView.getFileSystemView().getSystemTypeDescription(file)
var icon = FileSystemView.getFileSystemView().getSystemIcon(file)

在 Ubuntu (20.04) 但是,情况有所不同。到目前为止,我已经发现该图标内部有一个 ToolkitImage 而不是 BufferedImage,这很烦人,因为它是内部的 API,但我现在可以渲染它了。

剩下的问题是文件类型,当使用FileSystemView或returns“通用文件”时,Ubuntu上仍然returnsnull对于每个文件,如果使用 new FileChooser().getTypeDescription(file) 方式。

如何在 Ubuntu 上获得正确的文件类型描述?

getSystemTypeDescription的代码是

public String getSystemTypeDescription(File f) {
        return null;
}

WindowsFileSystemView 中被覆盖,但在 UnixFileSystemView 中没有。也许 JFileChooser 适合您的需求:

JFileChooser chooser = new JFileChooser();
String type = chooser.getTypeDescription(file);

我在执行 getSystemIcon 期间遇到 FileNotFoundException。按照代码,在方法 getShellFolder 中有这个片段

if (!Files.exists(Paths.get(file.getPath()), LinkOption.NOFOLLOW_LINKS)) {
            throw new FileNotFoundException();
}

所以没有遵循符号链接,也许这就是问题所在。但是,JFileChooser 再次起作用:

Icon icon = chooser.getIcon(file);

getFileSystemView坏了

一个大胆的声明:无论你想让它做什么,它都行不通。基于查看源代码。如果你愿意接受这是一条死胡同,你可以跳过这一部分,但我最好支持这样的说法,所以如果你想被说服,请继续阅读:

我这里的 JDK11 和 JDK17 的来源对 FileSystemView.getFileSystemView() 做了以下相对简单的方法:

  1. 如果File.separator的值为\,return则windows执行。
  2. 如果值为 /,return unix 实现(因此几乎所有其他内容,特别是包括 macs)。
  3. 否则return通用实现。让我们忘记这个,OS 此时既没有 / 也没有 \? pre-MacOSX mac os 早就死了,我能想到的只有这个了

Unix 实现是:

return null;

哎呀。那不会让我们走得太远。 windows 实现与 ShellFolder 一起使用。这是通用代码;我不明白为什么 unix 实现只是忽略它。

也许这个解释使 most 有意义:.getSystemTypeDescription 旨在 return OS 本身关于如何描述类型的意见这是文件的。 unix 实现只是 return null;s 的原因很简单,作为一个概念,这不是 unix 的工作方式。 OS 本身没有某种将文件扩展名映射到名称的注册表(例如 windows' HKEY_LOCAL_MACHINE/.txt 和朋友),也没有一个概念文件有自己的元数据,其中包含其他信息,例如 'which app created me' / 'which app should be used to open me when double clicked',例如 MacOSX。 (当然,如果你在 mac 上执行 运行,你仍然会得到 null,这确实是不可原谅的)。

当然,我们现在进入了一个更棘手的辩论:你的 OS 是什么,真的。人们可能会说“好吧,它的 linux,还有 KDE,或 GnomeDesktop 或诸如此类的东西,好吧,这就是这个应用程序,你知道的”。但也可以说您 运行 java 应用在 OS 'KDE/Linux' 上。换句话说,当我们谈论FileSystemView时,System是什么意思。显然,我正在查看的 JDK impl 源(即 OpenJDKs)选择 oses 将其定义为 'just linux',它没有诸如'系统对这是什么类型的文件的看法',使 return null; 正确,但 os 毫无用处,答案。

抽象超类型的 getSystemIcon 实现本身很奇怪:它是 getSystemTypeDescription 的 windows-specific 实现的近似副本 - 即:获取 ShellFolder 对象,然后询问它。我不知道为什么在 unix 上,'just ask the shellfolder' 是 getSystemIcon 的实现,而 'just return null' 是 getSystemTypeDescription 的实现 - 为什么不也问 shell 文件夹?

无论如何,即使你这样做了,也没有多大用处:默认 shell 文件夹实现 always returns null .这是 sun.awt 代码,因此针对该特定平台的 AWT 实现覆盖它的可能性要大得多,但无论如何,据我所知,这不在 openjdk 源代码中。

如果 ShellFolder return,getSystemIcon 的默认实现将 return 通用文件图标或通用文件夹图标(例如,通过调用 UIManager.getIcon("FileView.directotyIcon")) s null 作为图标。

所以让我们放弃这个实现:结论是它帮不了你。

定义'type description'

这到底是什么意思?我只能预见 3 个有用的观点来解释这是什么意思os:

  • 人类的眼球和大脑可能会理解的东西。
  • 一种 mime 类型,它是描述文件类型的通用标准。
  • “无论用户使用的 window 管理器在文件浏览器应用程序的本地等价物中看到什么 - explorer.exe,windows,Finder.app mac,等等。

推测 getSystemTypeDescription 是支持os回答第三个选项的方法(当地 window 经理的描述)。但是,鉴于 OpenJDK 实际上并没有实现这个(好吧,它确实以一种无用的方式,只是 returning null),你得到它的唯一方法是你把在相当大的努力中弄清楚每个流行的 window 经理是如何在世界范围内使用的,并将其全部移植到 java 代码中。我假设你对做那种工作不感兴趣。

但其他 2 个 - 有办法得到它。

让我们从 MIME 类型开始。

A方案是问java:

import java.nio.file.*;

class Test {
  public static void main(String[] args) throws Exception {
    var p = Paths.get("test.otf");
    Files.createFile(p);
    System.out.println(Files.probeContentType(p));
    Files.delete(p);
  }
}

保存到那个文件并运行它:java Test.java(是的JDK11+你可以将java文件传递给JVM可执行文件),和看看它是否有效。也就是说,应该 returning application/font-sfnt 适合你。无论如何,对我来说,Coretto JDK17 (java -version: openjdk version "17.0.3" 2022-04-19 LTS) on Ubuntu 20.04.1.

运行 它与 Temurin 17(来自 Adoptium 项目的 JDK)在 mac:font/otf 上。哦,好吧,这可能很尴尬。但这不一定是一个糟糕的答案不幸的是,Mac 自己的 Finder 应用程序有一个 'type description' 列,那是“OpenType® 字体”,而不是“font/otf”。据推测,macs 在某处有一个人类可读的描述数据库的 mimetype,据我所知,您无法使用通用 java 代码访问该数据库。不过,“font/otf”大概比“.otf 文件”好。

如果探测方法不适合您,您可以随时选择ose 来检查/etc/mime.types 是否存在,linuxen 上应该存在。对于每一行,.split("\s+")v[0] 是 mime 类型,其余元素均为不带点的扩展名,例如我的 ubuntu 安装会将 application/font-sfnt 列为类型 otfttf.

的 MIME 类型

另一种选择是发送一个已知的 extension-to-mimetype 映射列表。例如,Eclipse Jetty 有一个 MimeTypes class that is pre-filled with this sizable list of known extensions.

史蒂夫·乔布斯/闪存 MIME gang-sign

如果您像 Steve 或 MIME 协会一样,将 'the stuff after the last dot in the file name' 视为某种文件类型指示的整个过程会在您口中留下不好的味道,您希望躲开它。你可以,有点。无论如何在unixen上。 Most unix 安装有 /usr/bin/file - 我的 mac 和我正在查看的 ubuntu 安装都有这个。你可以 ProcessBuilder.exec 那。该工具根本不查看文件名,只查看实际内容。它可能很慢(如果需要的话会读取整个东西),但是,如果我 运行 它在一个 OTF 文件上,它会输出:

actual-valid-font-file.tof: OpenType font data

这肯定是一个我可以向用户显示的字符串,它比 font/otf 更“漂亮”,尽管它并不完全是原生 mac 应用程序显示的内容(显示 OpenType® font 如前所述。

在 windows 上,file(文件类型猜测器应用程序)通常不可用,嗯,听起来 FileSystemView.getFileSystemView().getSystemTypeDescription(file) 确实有效。我敢打赌 /usr/bin/file 不存在的系统数量, getSystemTypeDescription return 没有任何用处,是无限的。

图标

想必您在这里想要同样的东西:给我用户熟悉的图标,运行属于同一问题,尤其是 linux:每个 'file explorer' 应用程序有自己的图标集,还有 很多 文件浏览器应用程序 - 几乎每个 window 管理器都发布了自己的版本,并且有 很多 linux window 位经理。我不确定那里的任何 JVM impl 是否有代码可以从所有 ose 不同的 window 管理器实现中获取正确的图标,而且我认为没有标准化的方法来完成这个也可以只使用普通的 jane 磁盘访问。

但是,我们已经确定您可以选择 MIME 类型(如果使用 /usr/bin/file,则有 --mime-type 选项。(我的 /usr/bin/file 给了我 application/vnd.ms-opentype ,所以这已经是同一件事的 3 种不同的 mimetypes,男孩 'there are 14 different standards' 的整个 XKCD 漫画经常出现,不是吗)

给定一个 mime 类型,那里有大量免费和开源的图标集。

Oxygen icons 项目是一个 FOSS 图标集 hosted 在 github 上,带有一个图标(各种大小)用于大量 mimetypes。您可以先使用 .getSystemIcon,如果 return 不是一个合适的答案(有点棘手;有时您会得到一个您可能不想要的通用 'its a file' 图标),然后使用一个图标放。您不会匹配平台的 Look-n-Feel,但是如果这个问题真的只是“我想在 swing 中编写一个看起来与 host OS,无论是 windows、mac、KDE、Gnome、Xfce、Cinnamon、Budgie 还是 Enlightenment”,唯一务实的答案几乎只能是:“放弃那个白日梦吧”。

注意:海 :)