Java 使用哈希缩短长URL?

Java 使用哈希缩短长URL?,java,hash,url-shortener,Java,Hash,Url Shortener,我有一个文件缓存,文件从不同的URL下载。我想按每个文件的url名称保存它们。但这些名称可能相当长,而且我在一个使用FAT32文件系统的设备上——因此,在我耗尽实际磁盘空间之前,这些长名称就已经占用了资源 我正在寻找一种缩短文件名的方法,我已经得到了对字符串进行散列的建议。但我不确定哈希值对于两个不同的字符串是否保证是唯一的。如果两个散列URL的散列值相同,那么如果我不小心获取了错误的图像,那就太糟糕了 谢谢没有(缩短)散列可以保证每个输入有不同的散列。这根本不可能 我通常的做法是将原始名称保存

我有一个文件缓存,文件从不同的URL下载。我想按每个文件的url名称保存它们。但这些名称可能相当长,而且我在一个使用FAT32文件系统的设备上——因此,在我耗尽实际磁盘空间之前,这些长名称就已经占用了资源

我正在寻找一种缩短文件名的方法,我已经得到了对字符串进行散列的建议。但我不确定哈希值对于两个不同的字符串是否保证是唯一的。如果两个散列URL的散列值相同,那么如果我不小心获取了错误的图像,那就太糟糕了

谢谢

没有(缩短)散列可以保证每个输入有不同的散列。这根本不可能

我通常的做法是将原始名称保存在缓存文件的开头(例如,第一行)。因此,要在缓存中查找文件,请执行以下操作:

  • 散列URL
  • 查找与该哈希对应的文件
  • 检查第一行。如果与完整URL相同:
  • 文件的其余部分来自第二行并向前

也可以考虑在数据库中保存URL ->文件映射。对于这个算法,没有已知的故意引发冲突的方法,所以您应该是安全的。与两个具有公共结构(例如

http://
前缀)的数据片段发生冲突就更难了。如果在收到HTTP 200响应后保存这些内容,那么URL显然获取了一些内容,因此使用相同的SHA-1哈希值获取两个不同的、有效的URL真的不应该引起关注

如果有任何再保证,则使用它来标识源代码存储库中的所有对象、提交和文件夹。我还没有听说有人在对象存储中发生碰撞。

看看我的评论。
一种可能的解决方案(有很多)是创建一个本地文件(SQLite?XML?TXT?),在其中存储一对文件(file\u id-file\u name),这样您就可以将下载的文件以其唯一id保存为文件名。

仅仅是一个想法,不是最好的…

哈希不能保证是唯一的,但是冲突的可能性非常小

如果你的散列是,比如说,128位,那么任何一对条目发生冲突的几率是1/2^128。根据生日悖论,如果你的表中有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中,您可以使用一些数据结构高效地查找文件名(或替换为DB)

但我不确定哈希值对于两个不同的字符串是否保证是唯一的

它们在很大程度上不是(也不可能是,因为环境问题)。但是如果散列足够长(至少64位)并且分布良好(理想情况下是加密散列),那么冲突的可能性就会变得非常小,不值得担心

粗略地说,一旦文件数量接近可能的不同散列数()的平方根,冲突就有可能发生。因此,对于64位散列(10个字符的文件名),如果有40亿个文件,则发生一次冲突的几率约为50%

你必须决定这是否是一个可接受的风险。您可以通过延长散列的长度来减少冲突的可能性,但在某些情况下,这将意味着与您想要的相反。

您可以为每个URL生成一个URL,并将其用作文件名

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)));

我想你会发现散列文件名会有麻烦:散列(IMHO)会产生重复的条目……当你说“长名称在我耗尽实际磁盘空间之前就已经耗尽了资源”时,我有点怀疑。不知道为什么。但存储不是很便宜吗?@Marco同意,散列可以产生重复(“冲突”)。您应该创建一些冲突处理程序,在发生冲突时尝试一个新的哈希……哈希不能保证是唯一的——而一些哈希(例如md5或sha1)仅根据它们所包含的空间大小获得其“唯一性”。但是,它们可能一点也不短;-)@Marco,poplitea:散列可以产生冲突,但概率很小,完全可以忽略。即使您使用的是MD5(输出大小128位),那么对于10亿个条目,冲突的可能性仍然小于10^-18。即使是128位哈希也可能会破坏缩短文件名的初衷。Base64编码的长度仅为22个字符。如果这对于FAT32来说仍然太大,那么使用不同的文件系统可能是更好的解决方案。说真的,FAT32还在使用吗?FAT32可以有更长的文件名。这种担忧似乎与