【问题标题】:How would i generate a random and unique string in Ruby?我将如何在 Ruby 中生成一个随机且唯一的字符串?
【发布时间】:2013-03-05 17:50:38
【问题描述】:

在我正在开发的 Ruby on Rails 应用程序中,我允许用户上传文件并希望为这些文件提供一个简短的随机字母数字名称。 (例如“g7jf8”或“3bp76”)。最好的方法是什么?

我正在考虑从原始文件名和时间戳生成哈希/加密字符串。然后查询数据库以仔细检查它不存在。如果是,则生成另一个并重复。

我看到这种方法的问题是,如果重复字符串的概率很高,它可能会增加相当多的数据库负载。

【问题讨论】:

  • 还有两个请求同时尝试添加相同名称的潜在(如果不太可能)竞争条件。数据库应该对该列具有唯一约束,并且您应该准备好捕获ActiveRecord::RecordNotUnique
  • “随机”名称是否具有安全目的?如果没有,您还有更多选择。
  • 它不需要安全,但可以在 URL 中使用。感谢到目前为止的所有帮助。许多想法可以尝试和努力。

标签: ruby-on-rails ruby ruby-on-rails-3


【解决方案1】:

我用这个:)

def generate_token(column, length = 64)
  begin
    self[column] = SecureRandom.urlsafe_base64 length
  end while Model.exists?(column => self[column])
end

用您的型号名称替换 Model

【讨论】:

    【解决方案2】:
    SecureRandom.uuid
    

    会给你一个全局唯一的字符串。 http://en.m.wikipedia.org/wiki/Universally_unique_identifier

    SecureRandom.hex 32
    

    会给出一个随机字符串,但它的算法没有针对唯一性进行优化。当然,假设真正的随机性,与 32 位发生冲突的可能性基本上是理论上的。你可以每秒赚 10 亿美元,持续 100 年,但发生碰撞的几率只有 50%。

    【讨论】:

      【解决方案3】:

      使用 Ruby 的 SecureRandom.hex 函数以及您想要生成的可选字符数。

      【讨论】:

      • 我最喜欢这个问题的可能是SecureRandom.urlsafe_base64
      【解决方案4】:

      这将始终生成新的 uniq 40 大小的字母数字字符串,因为它也有时间戳。

      loop do
        random_token = Digest::SHA1.hexdigest([Time.now, rand(111..999)].join)
        break random_token unless Model.exists?(column_name: random_token)
      end
      

      注意:将模型替换为您的模型名称,将列名称替换为模型的任何现有列。

      【讨论】:

        【解决方案5】:

        您可以通过在每次添加新文件时递增它来分配唯一 id,并使用 OpenSSL::Cipher 将该 id 转换为加密字符串,并使用您保存在某处的常量密钥。

        【讨论】:

          【解决方案6】:

          如果您最终生成一个十六进制或数字摘要,您可以通过将数字表示为例如基数 62:

          # This is a lightweight base62 encoding for Ruby integers.
          B62CHARS = ('0'..'9').to_a + ('a'..'z').to_a + ('A'..'Z').to_a
          
          def base62_string nbr
            b62 = ''
            while nbr > 0
              b62 << B62CHARS[nbr % 62]
              nbr /= 62
            end
            b62.reverse
          end
          

          如果限制使用的字符集对您很重要(例如文件名中没有大写字符),那么只要您能找到一种输入合适随机数的方法,就可以轻松修改此代码。

          如果您的文件名应该是半安全的,您需要安排存储中的可能名称比实际名称多得多。

          【讨论】:

          • 这不会产生任何独特的东西,这是 OP 所要求的。给定相同的nbr,这将每次返回相同的字符串,并且您需要传递一个大得离谱的数字才能返回任何有效大小的字符串。例如:&gt; base62_string 99999999999999999999 # =&gt; "1V973MbJYWoT"
          • @Chrisbloom7:同意,这需要输入一个数字,我没有解释如何获得(但我确实在文中提到这是必需的)。不过,在 Ruby 中生成合适大小的随机数是微不足道的:SecureRandom.random_number(2**128)。该方法也适用于序列、哈希等。事实上,它需要大量输入来生成短字符串,这实际上是 OP 所需要的,他们要求使用短字符串
          【解决方案7】:

          看起来您实际上需要一个唯一的文件名,对吧?为什么不忘记复杂的解决方案而直接使用Time#nsec

          t = Time.now        #=> 2007-11-17 15:18:03 +0900
          "%10.9f" % t.to_f   #=> "1195280283.536151409"
          

          【讨论】:

          • 如果应用程序又大又忙,并且有几个独立的服务器,最终两个服务器会同时处理一个文件并发生冲突。
          • Concat nanosecs 与服务器名称完全确定。
          • 那时,我认为 SecureRandom.uuid 是一个更简单的解决方案。
          • 在我的情况下,您可以获得开箱即用的可排序列表。但这当然是口味问题。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-05-20
          • 2012-11-21
          • 2010-10-18
          • 2011-02-18
          • 2017-02-21
          • 2016-04-19
          相关资源
          最近更新 更多