如何将UUID缩短到特定长度?

13

我希望在数据库记录中使用UUID,但如果要用于URL,则希望其为5到8个字符。

我知道需要使用SecureRandombase64,但如何指定所需的长度?

3个回答

17

正如另一个答案所指出的那样,你无法将真实的UUID缩短为5-8个字符,但你可以将其缩短一些。 UUID是128位整数,即32个十六进制数字。每个字符可以轻松存储6位,并将长度缩短到22个字符,这就是基于64编码的方式。标准的base 64编码使用大小写字母、数字和“+”和“/”来完成。如果用“-”和“_”替换“+”和“/”,则最终得到的字符串就不必进行url编码了。你可以像这样做(使用UUIDTools来创建UUID):

uuid = UUIDTools::UUID.random_create
str = [uuid.raw].pack('m*').tr('+/','-_').slice(0..21)

要取回您的值:

(str + "==\n").tr('-_','+/').unpack('m*').first if str =~ /^[a-zA-Z0-9_\-]{22}$/

这是在假设UUID能被转换为原始格式,其中它是由16个8位字符组成的字符串。以下是一个irb会话,展示了一个真实的例子:

2.1.1 :016 > uuid=UUIDTools::UUID.random_create
 => #<UUID:0x83f1e98c UUID:20d07b6c-52af-4e53-afea-6b3ad0d0c627> 
2.1.1 :017 > uuid.raw
 => " \xD0{lR\xAFNS\xAF\xEAk:\xD0\xD0\xC6'" 
2.1.1 :018 > str = [uuid.raw].pack('m*').tr('+/','-_').slice(0..21)
 => "INB7bFKvTlOv6ms60NDGJw" 
2.1.1 :019 > uuid2 =  (str + "==\n").tr('-_','+/').unpack('m*').first
 => " \xD0{lR\xAFNS\xAF\xEAk:\xD0\xD0\xC6'" 
2.1.1 :022 > UUIDTools::UUID.parse_raw(uuid2)
 => #<UUID:0x849e6b44 UUID:20d07b6c-52af-4e53-afea-6b3ad0d0c627> 

我在多个网站上使用此方法,通常使用Postgres生成UUID作为表的主键并将其作为ID传递。这不会节省太多空间,但确实使一些URL适合一个80个字符的行,而标准格式的完整UUID则不会。带有连字符的标准UUID是36个字符,因此22个字符约为其大小的2/3。


1
确保您也查看下面的Luc Boissaye的答案。 - Michael Chaney

10
我根据Michael Chaney的方法创建了两个基于单行函数的函数。
def encode(uuid)     
  [uuid.tr('-', '').scan(/../).map(&:hex).pack('c*')].pack('m*').tr('+/', '-_').slice(0..21)                                                   
end
def decode(short_id)
 (short_id.tr('-_', '+/') + '==').unpack('m0').first.unpack('H8H4H4H4H12').join('-')                                                                                                                                                                                                                                                           
end

uuid = "355bf501-ffea-4f5a-a9e2-16074de6fcf2"                                                                                                                                                                                                                            
=> "355bf501-ffea-4f5a-a9e2-16074de6fcf2"                                                                                                                                                                          
encode(uuid)      
=> "NVv1Af_qT1qp4hYHTeb88g"        
decode(_)         
=> "355bf501-ffea-4f5a-a9e2-16074de6fcf2

如果我在我的Postgres数据库中存储缩短的UUID,会有问题吗? - Qasim
2
嗨@Qasim,你可以这样做,但我认为你将不得不将其存储为字符串,然后与postgresql本机uuid类型相比性能不会那么好。 - Luc Boissaye

2

我使用62个字符的字母表(Base62),因为我不想在我的缩短UUID中包含-_。UUID缩短程序将36位长的UUID缩短为22位长的字符串。我在Rails中使用此UUID缩短程序。按照您的需求更改字母表。

这是我的UUID缩短程序:

代码

# lib/uuid_shortener.rb
module UuidShortener
  ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
  ALPHABET_HASH = ALPHABET.each_char.with_index.each_with_object({}) { |(k, v), h| h[k] = v; }
  BASE = ALPHABET.length

  class << self
    def shorten(uuid)
      num = uuid.tr('-', '').to_i(16)
      return '0' if num.zero?
      return nil if num.negative?

      str = ''
      while num.positive?
        str = ALPHABET[num % BASE] + str
        num /= BASE
      end
      str
    end

    def expand(suid)
      num = i = 0
      len = suid.length - 1
      while i < suid.length
        pow = BASE**(len - i)
        num += ALPHABET_HASH[suid[i]] * pow
        i += 1
      end
      num.to_s(16).rjust(32, '0').unpack('A8A4A4A4A12').join('-')
    end
  end
end

用法

  1. 生成 UUID。
> uuid = SecureRandom.uuid
> uuid 
=> "2ff7b050-2a37-4d52-a8f0-76cffccbefe3"
  1. 短UUID
> suid = UuidShortener.shorten(uuid)
> suid
=> "1svPFI0god7vT7MNxKIrfR"
  1. 扩展SUID权限
> UuidShortener.expand(suid)
=> "2ff7b050-2a37-4d52-a8f0-76cffccbefe3"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接