2013-03-17 7 views
11

In einer Ruby on Rails App, an der ich arbeite, erlaube ich Benutzern, Dateien hochzuladen und diesen Dateien einen kurzen, zufälligen alphanumerischen Namen zu geben. (ZB 'g7jf8' oder '3bp76'). Was ist der beste Weg, dies zu tun?Wie würde ich eine zufällige und eindeutige Zeichenfolge in Ruby generieren?

Ich denke daran, eine Hash/verschlüsselte Zeichenfolge aus dem ursprünglichen Dateinamen und Timestamp zu generieren. Fragen Sie dann die Datenbank ab, um zu überprüfen, ob sie nicht existiert. Wenn dies der Fall ist, erzeuge ein anderes und wiederhole es.

Das Problem, das ich mit diesem Ansatz sehe, ist, wenn es hohe Wahrscheinlichkeit von doppelten Strings gibt, könnte es eine ziemlich lote der Datenbasis laden.

+1

Es gibt auch die potentielle (falls unwahrscheinliche) Race-Bedingung von zwei Anfragen, die versuchen, denselben Namen zur gleichen Zeit hinzuzufügen.Die Datenbank sollte eine eindeutige Einschränkung für diese Spalte haben und Sie sollten darauf vorbereitet sein, 'ActiveRecord :: RecordNotUnique' zu ​​fangen. – mpartel

+0

überprüfen http://StackOverflow.com/Questions/5966910/generate-unique-random-string-with-letters-and-numbers-in-lower-case – sameera207

+0

Hat der "zufällige" Name einen Sicherheitszweck? Wenn nicht, haben Sie mehr Möglichkeiten. –

Antwort

9

Ich benutze diese :)

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

ersetzen Model durch den Namen Ihres Modells

5

Verwenden Sie Rubys SecureRandom.hex-Funktion mit der optionalen Anzahl von Zeichen, die Sie generieren wollten.

+3

Mein Favorit für diese Frage könnte stattdessen "SecureRandom.urlsafe_base64" sein. –

0

Sie können eine eindeutige ID zuweisen, indem Sie sie jedes Mal, wenn eine neue Datei hinzugefügt wird, erhöhen und diese ID in eine verschlüsselte Zeichenfolge mit OpenSSL::Cipher mit einem konstanten Schlüssel konvertieren, den Sie irgendwo speichern.

0

Wenn Sie einen hexadezimalen oder numerischen Digest generieren, können Sie den Code kürzer halten, indem Sie die Zahl z. 62 Base:

# 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 

Wenn es wichtig ist für Sie den Zeichensatz verwendet zu beschränken (zB nicht in Großbuchstaben Zeichen in Dateinamen), dann kann dieser Code leicht angepasst werden, vorausgesetzt, können Sie eine Art der Ernährung finden in einer geeigneten Zufallszahl.

Wenn Ihre Dateinamen halbsicher sein sollen, müssen Sie dafür sorgen, dass es viel mehr mögliche Namen als tatsächliche Namen im Speicher gibt.

+0

Dies erzeugt nichts Einzigartiges, und darum hat das OP gebeten. Bei gleicher 'nbr' wird immer die gleiche Zeichenfolge zurückgegeben, und Sie müssen eine lächerlich große Zahl übergeben, um eine Zeichenfolge beliebiger Größe zurückzugeben. Bsp: '> base62_string 99999999999999999999 # =>" 1V973MbJYWoT "' –

+0

@ Chrisbloom7: Einverstanden, das braucht Fütterung in einer Zahl, die ich nicht erkläre, wie man es bekommt (aber ich erwähne das im Text ist dies erforderlich). Das Erzeugen einer zufälligen Zahl geeigneter Größe ist jedoch in Ruby trivial: 'SecureRandom.random_number (2 ** 128)'. Der Ansatz funktioniert auch mit Sequenzen, Hashes usw. Die Tatsache, dass es eine große Eingabe erfordert, um eine kurze Zeichenfolge zu machen, ist für das OP tatsächlich wünschenswert, sie fragten nach einer kurzen Zeichenfolge –

8
SecureRandom.uuid 

Gibt Ihnen einen weltweit einzigartigen String. http://en.m.wikipedia.org/wiki/Universally_unique_identifier

SecureRandom.hex 32 

eine zufällige Zeichenfolge geben, aber es ist Algorithmus nicht auf Eindeutigkeit optimiert ist. Natürlich ist die Wahrscheinlichkeit einer Kollision mit 32 Ziffern unter der Annahme einer echten Zufälligkeit grundsätzlich theoretisch. Sie könnten 1 Milliarde pro Sekunde für 100 Jahre machen und haben nur eine 50% ige Chance auf eine Kollision.

-1

Es sieht so aus als ob Sie tatsächlich einen eindeutigen Dateinamen brauchen, richtig? Warum nicht komplexe Lösungen vergessen und einfach Time#nsec verwenden?

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

Wenn die App groß und beschäftigt ist, und mehrere unabhängige Server hat , schließlich werden zwei gleichzeitig eine Datei verarbeiten und eine Kollision bekommen. –

+0

Concat _nanosecs_ mit dem Namen des Servers, um absolut sicher zu sein. – mudasobwa

+1

An diesem Punkt ist SecureRandom.uuid eine einfachere Lösung, denke ich. –

0

Dies wird immer wieder neue uniq 40 Größe alphanumerische Zeichenfolge erzeugen, weil es auch Zeitstempel hat.

do-Schleife

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

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

Ende

Hinweis: Ersetzen Modell von Ihrem model_name und Spalten_Name durch jede vorhandene Spalte Ihres Modells.

Verwandte Themen