2013-03-17 32 views
11

Trong ứng dụng Ruby on Rails tôi đang làm việc trên tôi cho phép người dùng tải tệp lên và muốn cung cấp cho các tệp này một tên ngắn và ngẫu nhiên. (Ví dụ: 'g7jf8' hoặc '3bp76'). Cách tốt nhất để làm việc này là gì?Làm cách nào để tạo một chuỗi ngẫu nhiên và duy nhất trong Ruby?

Tôi nghĩ đến việc tạo chuỗi băm/mã hóa từ tên tệp và dấu thời gian gốc. Sau đó truy vấn cơ sở dữ liệu để kiểm tra lại nó không tồn tại. Nếu có, tạo một cái khác và lặp lại.

Vấn đề tôi thấy với cách tiếp cận này là nếu có khả năng chống chịu cao của các chuỗi trùng lặp, nó có thể thêm khá nhiều lượt tải cơ sở dữ liệu.

+1

Ngoài ra còn có điều kiện đua tiềm năng (nếu không thể xảy ra) của hai yêu cầu cố gắng thêm cùng một tên cùng một lúc.Cơ sở dữ liệu nên có một ràng buộc duy nhất trên cột đó và bạn nên chuẩn bị để bắt 'ActiveRecord :: RecordNotUnique'. – mpartel

+0

kiểm tra http://stackoverflow.com/questions/5966910/generate-unique-random-string-with-letters-and-numbers-in-lower-case – sameera207

+0

Tên "ngẫu nhiên" có mục đích bảo mật không? Nếu không, bạn có nhiều lựa chọn hơn. –

Trả lời

9

tôi sử dụng này :)

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

Thay Model theo tên mô hình của bạn

5

Sử dụng chức năng SecureRandom.hex của Ruby với số ký tự tùy chọn bạn muốn tạo.

+3

Thay vào đó, yêu thích của tôi cho câu hỏi này có thể là 'SecureRandom.urlsafe_base64'. –

0

Bạn có thể chỉ định một id duy nhất bằng cách tăng nó mỗi lần thêm tệp mới và chuyển id đó thành chuỗi được mã hóa bằng cách sử dụng OpenSSL::Cipher bằng khóa không đổi mà bạn lưu ở đâu đó.

0

Nếu bạn kết thúc việc tạo thông số thập lục phân hoặc số, bạn có thể giữ mã ngắn hơn bằng cách biểu thị số đó như ví dụ: Cơ sở 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 

Nếu điều quan trọng là để bạn có thể hạn chế bộ ký tự được sử dụng (ví dụ như không có ký tự chữ hoa trong tên tập tin), sau đó mã này có thể dễ dàng được điều chỉnh, miễn là bạn có thể tìm thấy một cách để nuôi dưỡng trong một số ngẫu nhiên phù hợp.

Nếu tên tệp của bạn được cho là bán an toàn, bạn cần sắp xếp có nhiều tên có thể có hơn tên thực trong bộ nhớ.

+0

Điều này không tạo ra bất kỳ điều gì độc đáo, đó là những gì mà OP yêu cầu. Với cùng một 'nbr', điều này sẽ trả về cùng một chuỗi mỗi lần, và bạn cần phải vượt qua một số lớn vô lý để trả về một chuỗi có kích thước đáng kể. Ví dụ: '> base62_string 99999999999999999999 # =>" 1V973MbJYWoT "' –

+0

@ Chrisbloom7: Đồng ý, điều này cần cho ăn trong một số, mà tôi không giải thích làm thế nào để có được (nhưng tôi đề cập đến trong văn bản này là bắt buộc). Việc tạo ra một số ngẫu nhiên kích thước phù hợp là tầm thường trong Ruby mặc dù: 'SecureRandom.random_number (2 ** 128)'. Cách tiếp cận này cũng hoạt động với chuỗi, băm vv Thực tế là nó đòi hỏi một đầu vào lớn để tạo ra một chuỗi ngắn thực sự là mong muốn cho OP, họ yêu cầu một chuỗi ngắn –

8
SecureRandom.uuid 

Sẽ cung cấp cho bạn chuỗi duy nhất toàn cầu. http://en.m.wikipedia.org/wiki/Universally_unique_identifier

SecureRandom.hex 32 

Sẽ đưa ra một chuỗi ngẫu nhiên, nhưng thuật toán của nó không được tối ưu hóa cho tính duy nhất. Tất nhiên cơ hội va chạm với 32 chữ số, giả sử sự thật ngẫu nhiên, về cơ bản là lý thuyết. Bạn có thể kiếm 1 tỷ mỗi giây trong 100 năm và chỉ có 50% cơ hội va chạm.

-1

Dường như bạn thực sự cần một tên tệp duy nhất, phải không? Tại sao không quên về các giải pháp phức tạp và chỉ cần sử dụng Time#nsec?

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

Nếu ứng dụng lớn và bận, và có một số máy chủ độc lập , cuối cùng hai sẽ xử lý một tập tin cùng một lúc và nhận được một vụ va chạm. –

+0

Concat _nanosecs_ với tên của máy chủ là hoàn toàn chắc chắn. – mudasobwa

+1

Tại thời điểm đó, SecureRandom.uuid là một giải pháp đơn giản hơn tôi nghĩ. –

0

này sẽ luôn tạo ra uniq 40 kích thước chuỗi alpha-số mới, bởi vì nó có tem Time cũng có.

vòng lặp làm

random_token = Digest::SHA1.hexdigest([Time.now, rand(111..999)].join) 

    break random_token unless Model.exists?(column_name: random_token) 

cuối

Lưu ý: Thay thế mẫu bằng model_name và column_name của bạn bằng bất kỳ cột hiện tại của mô hình của bạn.

Các vấn đề liên quan