#mime #media-type #file-tree #load-file

bin+lib tree_magic_fork

通过遍历文件类型树来确定文件的 MIME 类型

1 个不稳定版本

使用旧的 Rust 2015

0.2.2 2019 年 6 月 4 日

文件系统 中排名 #1205

每月下载量 30
rga 中使用

MIT 许可证

64KB
1K SLoC

包含 (Zip 文件,1KB) benches/application/zip

tree_magic

tree_magic 是一个 Rust crate,用于确定给定文件或字节数据流的 MIME 类型。

请参阅https://docs.rs/tree_magic/ 的文档

稳定版用户:即使您将其用作库,您可能还需要包含 cli 特性标志!(这在 nightly 上已修复)

与 libmagic 和 file(1) 的典型方法不同,此方法根据子类加载基于树的文件类型。(例如:application/vnd.openxmlformats-officedocument.wordprocessingml.document (MS Office 2007) 子类 application/zip,它又是 application/octet-stream 的子类)然后,它不会检查每个文件类型,而是可以遍历树,仅检查需要检查的文件类型。(毕竟,最快的检查是根本不运行的检查。)

此库还提供了一种检查文件是否为特定类型的能力,而无需对每个文件类型进行检查。

还提供了一个简单的命令行客户端 tmagic,它充当 file --mime-type 的替代品,不包括字符集信息。

性能

这非常快。

这是在 OpenSUSE Tumbleweed 上对我的下载文件夹(抱歉,找不到好的公开可用随机文件集)的测试。 tmagic 使用 cargo build --release 编译,而 file 来自 OpenSUSE 仓库。这是一个热运行,这意味着我已多次运行这两个程序。系统是双核 Intel Core i7 640M,结果使用 time 进行测量。

程序 实际 用户 系统
tmagic 0.2.0 0m0.063s 0m0.052s 0m0.004s
file-5.30 --mime-type 0m0.924s 0.800s 0.116s

有几个原因导致了这种情况。主要是

  • 由于图方法,需要解析的类型更少。

  • 首先加载文件的前4K内容,然后传递给所有解析器,而不是不断从磁盘重新加载。这样做时,所需时间大约在0.130秒左右。

  • 在检查罕见类型之前,先检查最常见的类型(如image/png、image/jpeg、application/zip等)。

  • 所有可以由lazy_static!处理的都进行处理。

夜间用户也可以运行 cargo bench 来执行一些基准测试。对于同一硬件上的tree_magic 0.2.0

test from_u8::application_zip  ... bench:      17,086 ns/iter (+/- 845)
test from_u8::image_gif        ... bench:       5,027 ns/iter (+/- 520)
test from_u8::image_png        ... bench:       4,421 ns/iter (+/- 1,795)
test from_u8::text_plain       ... bench:     112,578 ns/iter (+/- 11,778)
test match_u8::application_zip ... bench:         222 ns/iter (+/- 144)
test match_u8::image_gif       ... bench:         140 ns/iter (+/- 14)
test match_u8::image_png       ... bench:         139 ns/iter (+/- 18)
test match_u8::text_plain      ... bench:          44 ns/iter (+/- 3)

然而,需要注意的是,FreeDesktop.org的magic文件比libmagic使用的magic文件少。在我的系统上,tree_magic支持400种类型,而 /usr/share/misc/magic 包含了855个 !:mime 标签。)然而,它解析起来要简单得多,因为它只涵盖魔术数字,而不是属性或其他内容。有关修复此问题的计划,请参阅TODO部分。

兼容性

这已在Windows 7和OpenSUSE Tumbleweed Linux上使用Rust Stable和Nightly进行了测试。

所有MIME信息和关系信息都是从Shared MIME-info数据库中加载的,具体请参阅https://specifications.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html中所述。如果您认为您的系统上没有此数据库,请关闭 sys_fdo_magic 功能标志。

这提供了最常见的文件类型,但仍缺少一些重要的类型,如LibreOffice或MS Office 2007+支持或ISO文件。随着zip检查器的添加,预期这将会改进。

架构

tree_magic 被分成不同的“检查器”模块。每个检查器处理一组特定的文件类型,仅处理这些类型。例如,basetype 检查器处理 inode/*text/plain 类型,而 fdo_magic 检查器处理任何具有魔术数字的文件。这里的想法是,我们可以在不遵循 libmagic 的方式(即一个适合所有文件的魔术描述符格式)的情况下进行专业化,并选择最适合文件格式的检查器。

在库初始化期间,每个检查器都会查询其支持的类型以及它们之间的父-子关系。在此期间,检查器可以加载任何规则、模式等到内存中。这里的一个大哲学是,检查阶段的时间比初始化阶段的时间宝贵得多。库只初始化一次,库可以在程序的生命周期中检查成千上万的文件。

根据文件类型和关系的列表,构建一个有向图,并将每个节点添加到哈希映射中。库用户可以直接使用这些信息来查找给定MIME的父节点、子节点等。

当需要将文件与某种MIME(match_*)进行比较时,将查询每个检查器以查看它是否支持该类型,如果支持,则运行检查器。如果检查器返回true,则它必须是那种类型。

当需要找到文件的MIME类型(from_*)时,库从类型图中的 all/all 节点(或用户指定的任何节点)开始,沿着树向下遍历。如果找到匹配项,它将继续沿着该分支搜索。如果没有找到匹配项,它将检索找到的最深MIME类型。

TODO

改进fdo-magic检查器

目前,fdo-magic 检查器不处理字节序。它也不处理存储在用户主目录中的魔术文件。

额外的检查器

计划为许多类型提供自定义文件检查函数。以下是一些想法

  • zip:任何子类于 application/zip 的内容都可以通过查看 zip 文件夹的列表来进一步确定。

  • grep:程序脚本和配置文件等文本文件可以使用正则表达式(或任何最适合的工具)进行解析。

  • jsontomlxml 等:将给定的文件与模式进行比较,如果匹配则返回 true。(此时应该很少有潜在的匹配项,所以加载整个文件应该是可以的)

  • (专用解析器):没有魔法的二进制(或文本)文件可以与快速且简单的 nom 解析器进行比较,而不是使用 libmagic 的奇怪启发式方法。

要添加额外的检查器类型,添加一个新的模块导出

  • init::get_supported() -> Vec<(String)>

  • init::get_subclasses() -> Vec<String, String)>

  • test::from_u8(&[u8], &str) -> bool

  • test::from_filpath(&str, &str) -> Result<bool, std::io::Error>

然后将这些函数的引用添加到 lib.rs 中的 CHECKERS lazy_static! 中。最底层的条目首先被搜索。

缓存

从现在起,对于检查器(如 basetype 的元数据或 json/toml/xml 示例)来说,能够缓存文件的内存表示非常重要,这样就不必为每个新类型重新加载和重新解析。在当前架构下,这相当难以实现。

多种文件类型

有些文件非常奇怪(比如 多语言 quines)。这些文件可能是多种文件类型。出于安全原因,这可能值得处理。(但这不是首要任务。)

并行处理

目前这是单线程的。这是一个令人尴尬的并行任务(多个文件、多个类型、每种类型的多个规则...),所以应该有巨大的速度优势。

不去做的事

文件属性

libmagicfile 默认会打印出描述文件类型的字符串,以及像 JPEG 图片或 ELF 文件之类的数据的全部元数据。这不是 tree_magic 将会支持的,因为它完全是多余的。属性的支持最好在一个独立的 crate 中处理,该 crate 可以根据 MIME 提取可预测的、机器可读的格式。

依赖关系

~3–11MB
~94K SLoC