我有一个文件缓存,文件是从不同的 url 下载的。我想用它们的 url 名称保存每个文件。不过,这些名称可能很长,而且我在使用 FAT32 文件系统的设备上 - 所以在我用完实际磁盘空间之前,这些长名称会占用资源。
我正在寻找一种缩短文件名的方法,已经得到了散列字符串的建议。但我不确定哈希是否保证对于两个不同的字符串是唯一的。如果两个散列 url 得到相同的散列值,我不小心获取了错误的图像,那就太糟糕了。
谢谢
我有一个文件缓存,文件是从不同的 url 下载的。我想用它们的 url 名称保存每个文件。不过,这些名称可能很长,而且我在使用 FAT32 文件系统的设备上 - 所以在我用完实际磁盘空间之前,这些长名称会占用资源。
我正在寻找一种缩短文件名的方法,已经得到了散列字符串的建议。但我不确定哈希是否保证对于两个不同的字符串是唯一的。如果两个散列 url 得到相同的散列值,我不小心获取了错误的图像,那就太糟糕了。
谢谢
您可以为每个 URL 生成一个UUID并将其用作文件名。
UUID 是唯一的(或“实际上是唯一的”)并且长度为 36 个字符,所以我想文件名不会有问题。
从版本 5 开始,JDK 附带了一个生成 UUID 的类 (java.util.UUID)。如果有办法将 UUID 与 URL 相关联,您可以使用随机生成的 UUID,或者您可以使用基于名称的 UUID。基于名称的 UUID 始终相同,因此以下始终为真:
String url = ...
UUID urlUuid = UUID.nameUUIDFromBytes(url.getBytes);
assertTrue(urlUuid.equals(UUID.nameUUIDFromBytes(url.getBytes)));
没有(缩短)散列可以保证每个输入都有不同的散列。这根本不可能。
我通常这样做的方法是将原始名称保存在缓存文件的开头(例如,第一行)。因此,要在缓存中查找文件,您可以这样做:
您还可以考虑将 URL-> 文件映射保存在数据库中。
但我不确定哈希是否保证对于两个不同的字符串是唯一的。
他们不是(而且不可能,由于鸽子原则)。但是如果散列足够长(至少 64 位)并且分布良好(理想情况下是加密散列),那么发生冲突的可能性就会变得非常小,以至于不值得担心。
作为粗略的指导方针,一旦文件数量接近可能的不同哈希数的平方根(生日悖论),冲突就很可能发生。因此,对于 64 位散列(10 个字符的文件名),如果您有 40 亿个文件,您有大约 50% 的机会发生一次冲突。
您必须决定这是否是可接受的风险。您可以通过延长哈希值来减少冲突的机会,但当然在某些时候这将意味着与您想要的相反。
散列不能保证是唯一的,但发生冲突的机会非常小。
如果您的哈希是 128 位,那么任何一对条目发生冲突的机会是 2^128 中的 1。根据生日悖论,如果你的表中有 10^18 个条目,那么发生冲突的几率只有 1%,所以你真的不需要担心。如果您特别偏执,请使用 SHA256 或 SHA512 增加散列的大小。
显然,您需要确保散列表示实际上比原始文件名占用更少的空间。Base-64 编码的字符串表示每个字符 6 位,因此您可以进行数学运算以确定是否值得首先进行哈希处理。
如果您的文件系统因为名称太长而出错,那么您可以为实际存储创建前缀子目录。例如,如果文件映射了哈希 ABCDE,那么您可以将其存储为/path/to/A/B/CDE
,或者可能/path/to/ABC/DE
取决于最适合您的文件系统的文件。
Git 是这种技术在实践中的一个很好的例子。
您可以做的是通过索引保存文件并使用索引文件查找实际文件的位置
在您拥有的目录中:
index.txt
file1
file2
...
etc.
在 index.txt 中,您使用一些数据结构来有效地查找文件名(或用数据库替换)
看我的评论。
一种可能的解决方案(有很多)是创建一个本地文件(SQLite?XML?TXT?),在其中存储一对(file_id - file_name),以便您可以将下载的文件及其唯一 ID 保存为文件名。
只是一个想法,不是最好的...