如何为URL生成唯一的哈希?

时间:2009-10-27 08:22:02

标签: algorithm

鉴于来自twitter的这两张图片。

http://a3.twimg.com/profile_images/130500759/lowres_profilepic.jpg
http://a1.twimg.com/profile_images/58079916/lowres_profilepic.jpg

我想将它们下载到本地文件系统&将它们存储在一个目录中。 我该如何克服姓名冲突?

在上面的示例中,我无法将它们存储为 lowres_profilepic.jpg 。 我的设计理念是将URL视为不透明字符串,但最后一段除外。 我可以使用哪些算法(实现为 f )将前缀散列为唯一字符串。

f( "http://a3.twimg.com/profile_images/130500759/" ) = 6tgjsdjfjdhgf
f( "http://a1.twimg.com/profile_images/58079916/" )  = iuhd87ysdfhdk

这样,我可以将文件保存为: -

6tgjsdjfjdhgf_lowres_profilepic.jpg
iuhd87ysdfhdk_lowres_profilepic.jpg

我不需要加密算法,因为它需要是一个高效的操作。

12 个答案:

答案 0 :(得分:17)

无论您如何操作(散列,编码,数据库查找),我建议您尝试将大量URL映射到大型平面目录中的文件。

原因是大多数文件系统的文件查找涉及对目录中文件名的线性扫描。因此,如果您的所有N个文件都在一个目录中,则查找将平均涉及1/2 N个比较;即O(N)(请注意,ReiserFS将目录中的名称组织为BTree。但是,ReiserFS似乎是例外而不是规则。)

最好将URI映射到目录树,而不是一个大的平面目录。根据树的形状,查找可以与O(logN)一样好。例如,如果您组织树以使其具有3级目录,每个目录中最多100个条目,则可以容纳100万个URL。如果您将映射设计为使用2个字符的文件名,则每个目录应该很容易放入单个磁盘块中,并且路径名查找(假设所需的目录已经被缓存)应该花费几微秒。

答案 1 :(得分:10)

您真正想要的是拥有一个不会与其他人发生冲突的合法文件名。

  • 网址的任何编码都可以使用,甚至是base64:例如: filename = base64(url)
  • 加密哈希将为您提供您想要的内容 - 尽管您声称这将是性能瓶颈,但在您进行基准测试之前不确定

答案 2 :(得分:4)

哈希的本质是它可能导致冲突。这些替代方案之一如何:

  1. 使用目录树。从字面上为URL的每个组件创建子目录。
  2. 生成唯一ID。这里的问题是如何保持实名和保存的id之间的映射。您可以使用在URL和生成的唯一ID之间映射的数据库。您只需将记录插入到生成唯一ID的数据库中,然后将该id用作文件名。

答案 3 :(得分:4)

URL的一个关键概念是它是唯一的。为什么不用它?

每个缩短信息的算法都会产生冲突。可能不太可能,但仍有可能

答案 4 :(得分:4)

一种非常简单的方法:

f( "http://a3.twimg.com/profile_images/130500759/" ) = a3_130500759.jpg
f( "http://a1.twimg.com/profile_images/58079916/" )  = a1_58079916.jpg

由于此URL的其他部分是常量,您可以使用子域,查询路径的最后一部分作为唯一文件名。

不知道此解决方案可能存在什么问题

答案 5 :(得分:2)

虽然CRC32产生的最大值为2 ^ 32,无论您的输入如何,因此不会避免冲突,但对于这种情况,它仍然是可行的选择。

速度很快,因此如果生成冲突的文件名,只需在URL中添加/更改一个字符,然后重新计算CRC。

43亿个可能的校验和意味着文件名冲突的可能性,当与原始文件名结合时,将会在正常情况下变得不重要。

我自己也用这种方法做了类似的事情并对表现很满意。 见Fast CRC32 in Software.

答案 6 :(得分:2)

您可以在Java中使用UUID类从字节生成UUID中的任何内容,这是唯一的,您不会遇到文件查找问题

String url = http://www.google.com;
String shortUrl = UUID.nameUUIDFromBytes("http://www.google.com".getBytes()).toString();

答案 7 :(得分:1)

我看到你的问题是这个问题的最佳哈希算法是什么。您可能需要查看此Best hashing algorithm in terms of hash collisions and performance for strings

答案 8 :(得分:1)

git内容管理系统基于 SHA1 ,因为它具有非常小的冲突机会。

如果它对git有好处,那么对你来说是好事。

答案 9 :(得分:1)

我正在使用他们的缓存脚本的修改版本与thumbalizr一起玩,我认为它有一些很好的解决方案。代码在github.com/mptre/thumbalizr上,但是短版本是使用md5来构建文件名,它从文件名中获取前两个字符并使用它来创建一个名为完全相同的文件夹。这意味着很容易打破文件夹,并快速找到没有数据库的相应文件夹。有点让我头脑简单。

它生成这样的文件名 http://pappmaskin.no/opensource/delicious_snapcasa/mptre-thumbalizr/cache/fc/fcc3a328e0f4c1b51bf5e13747614e7a_1280_1024_8_90_250.png

最后一部分_1280_1024_8_90_250与脚本在与thumbalizr api交谈时使用的不同设置相匹配,但我猜fcc3a328e0f4c1b51bf5e13747614e7a是网址的直接md5,在​​本例中为thumbalizr.com

我尝试更改配置以生成200px宽的图像,并且该图像位于同一文件夹中,但不是_250.png而是称为_200.png

我没有时间在代码中挖掘那么多东西,但我确信它可以从thumbalizr逻辑中分离出来并且更加通用。

答案 10 :(得分:0)

你说:

  

我不需要加密算法,因为它需要是一个高效的操作。

嗯,我理解你对速度的需求,但我认为你需要考虑你的方法的缺点。如果你只需要为url创建哈希,你应该坚持使用它,而不是编写一个新算法,例如,你需要处理冲突。

所以你可以让Dictionary<string, string>作为你网址的缓存。因此,当您获得新地址时,首先在该列表中进行查找,如果找不到匹配项,请将其哈希并存储以备将来使用。

按照这一行,您可以尝试MD5:

public static void Main(string[] args)
{
    foreach (string url in new string[]{ 
        "http://a3.twimg.com/profile_images/130500759/lowres_profilepic.jpg", 
        "http://a1.twimg.com/profile_images/58079916/lowres_profilepic.jpg" })
    {
        Console.WriteLine(HashIt(url));
    }
}

private static string HashIt(string url)
{
    Uri path = new Uri(new Uri(url), ".");
    MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
    byte[] data = md5.ComputeHash(
        Encoding.ASCII.GetBytes(path.OriginalString));
    return Convert.ToBase64String(data);
}

你会得到:

rEoztCAXVyy0AP/6H7w3TQ==
0idVyXLs6sCP/XLBXwtCXA==

答案 11 :(得分:0)

twimg.com网址的数字部分似乎已经是每张图片的唯一值。我的研究表明这个数字是连续的(即下面的示例网址是有史以来上传的433,484,366个个人资料图片 - 恰好是我的)。因此,这个数字是独一无二的。我的解决方案是简单地使用文件名的数字部分作为“哈希值”,不用担心会找到非唯一值。

  • 网址:http://a2.twimg.com/profile_images/433484366/terrorbite-industries-256.png
  • 文件名:433484366.terrorbite-industries-256.png
  • 唯一ID:433484366

我已经将此系统用于显示新推文通知的Python脚本,并且作为其操作的一部分,它会缓存配置文件图像缩略图以减少不必要的下载。

P.S。下载图像的子域名没有区别,所有子图像都可以使用所有图像。