Was ist der beste Weg, um einen eindeutigen und kurzen Dateinamen in Java zu generieren?

73

Ich möchte nicht unbedingt UUIDs verwenden, da diese ziemlich lang sind.

Die Datei muss nur in ihrem Verzeichnis eindeutig sein.

Ein Gedanke, der mir in den Sinn kommt, ist die Verwendung File.createTempFile(String prefix, String suffix), aber das scheint falsch zu sein, da die Datei nicht temporär ist.

Der Fall von zwei Dateien, die in derselben Millisekunde erstellt wurden, muss behandelt werden.

Jeff Bloom
quelle
5
Achten Sie nicht zu sehr auf den Teil "Temp" des Namens. Lesen Sie Javadocs, um zu sehen, dass es wirklich mehr um die Einzigartigkeit geht, die häufig für temporäre Dateien benötigt wird. Aber nicht unbedingt nur für sie.
StaxMan

Antworten:

92

Nun, Sie könnten die 3-Argument-Version verwenden, mit File.createTempFile(String prefix, String suffix, File directory)der Sie sie dort platzieren können, wo Sie möchten. Wenn Sie es nicht sagen, wird Java es nicht anders behandeln als jede andere Datei. Der einzige Nachteil ist, dass der Dateiname garantiert mindestens 8 Zeichen lang ist (mindestens 3 Zeichen für das Präfix plus 5 oder mehr von der Funktion generierte Zeichen).

Wenn Ihnen das zu lang ist, können Sie wahrscheinlich immer mit dem Dateinamen "a" beginnen und "b", "c" usw. durchlaufen, bis Sie einen finden, der noch nicht vorhanden ist.

Pesto
quelle
2
Aber würde es die Einzigartigkeit zwischen mehreren Programmläufen garantieren?
Reddy
6
Gemäß den Dokumenten wird garantiert, dass (1) die durch den zurückgegebenen abstrakten Pfadnamen angegebene Datei vor dem Aufrufen dieser Methode nicht vorhanden war und (2) weder diese Methode noch eine ihrer Varianten denselben abstrakten Pfadnamen erneut in der zurückgeben aktueller Aufruf der virtuellen Maschine. Wenn Sie also Dateien im selben Verzeichnis erstellen und nicht löschen, sind sie eindeutig.
Spike Williams
löscht es die Datei nach dem Schließen? oder müssen Sie es löschen, wenn Sie damit fertig sind?
Lucke
Sie müssen die Datei löschen. Datei testFile = File.createTempFile ("MyApp", ".tmp", outputDirectory); testFile.delete ();
Alchemistmatt
@Lucke deleteOnExit ()
lainatnavi
28

Ich würde die Apache Commons Lang-Bibliothek verwenden (http://commons.apache.org/lang ).

Es gibt eine Klasse org.apache.commons.lang.RandomStringUtils, mit der zufällige Zeichenfolgen mit einer bestimmten Länge generiert werden können. Sehr praktisch, nicht nur zur Generierung von Dateinamen!

Hier ist das Beispiel:

String ext = "dat";
File dir = new File("/home/pregzt");
String name = String.format("%s.%s", RandomStringUtils.randomAlphanumeric(8), ext);
File file = new File(dir, name);
Tomasz Błachowicz
quelle
7
@marcolopes: Aber die Chancen für gleiche Namen für zwei Dateien sind extrem gering. Falls wir 62 verschiedene Zeichen haben (ich weiß nicht, wie viele RandomStringUtils verwenden; 62 ist meine Vermutung für Groß- und Kleinschreibung), ist es 62 ^ n, wobei n Ihre Dateinamenlänge ist. Für das obige Beispiel mit einer Länge von 8 wäre die Chance 2.183401056 × 10¹⁴.
Kris
@Kris dennoch, mit ein paar Codezeilen könnten Sie eine 100% ige Garantie für die Eindeutigkeit haben (oder indem Sie einfach eine der vorhandenen Methoden verwenden).
Xorinzor
Dies ist kein idiomatischer Weg. Du vermeidest es besser. Verwenden Sie wie vorgeschlagen temporäre Dateien .
Alexander Pozdneev
Alle anderen Bibliotheken in das Projekt aufnehmen, um einen zufälligen Dateinamen zu generieren? Keine tragfähige Lösung, sage ich.
Stunner
13

Ich benutze den Zeitstempel

dh

new File( simpleDateFormat.format( new Date() ) );

Und lassen Sie das simpleDateFormat wie folgt initialisieren:

new SimpleDateFormat("File-ddMMyy-hhmmss.SSS.txt");

BEARBEITEN

Wie wäre es mit

new File(String.format("%s.%s", sdf.format( new Date() ),
                                random.nextInt(9)));

Es sei denn, die Anzahl der in derselben Sekunde erstellten Dateien ist zu hoch.

Wenn das der Fall ist und der Name keine Rolle spielt

 new File( "file."+count++ );

: P.

OscarRyz
quelle
12
Ja, aber was ist, wenn zwei Dateien in derselben Sekunde oder Millisekunde erstellt werden?
Jeff Bloom
1
Millisekunde ist nicht wahrscheinlich, und Sie könnten einen Timer darauf setzen, um zu verhindern, dass Dateien in der gleichen Sekunde erstellt werden ...
jesses.co.tt
1
@ JeffBloom Ich bin auf ein Problem gestoßen, das dem ähnelt, was Sie fragen. Ich erstelle eine Datei und der Dateiname enthält System.currentTimeMillis (). Angenommen, ich kopiere die Datei und füge eine andere Datei mit demselben System.currentTimeMillis () ein. Meine nächste Datei wird nicht gelesen. Wie kann ich nicht zulassen, dass Benutzer Dateien kopieren und einfügen, die dieselbe System.currentTimeMillis () haben? Jede Hilfe wäre sehr dankbar
10

Das funktioniert bei mir:

String generateUniqueFileName() {
    String filename = "";
    long millis = System.currentTimeMillis();
    String datetime = new Date().toGMTString();
    datetime = datetime.replace(" ", "");
    datetime = datetime.replace(":", "");
    String rndchars = RandomStringUtils.randomAlphanumeric(16);
    filename = rndchars + "_" + datetime + "_" + millis;
    return filename;
}

// USE:

String newFile;
do{
newFile=generateUniqueFileName() + "." + FileExt;
}
while(new File(basePath+newFile).exists());

Ausgabedateinamen sollten folgendermaßen aussehen:

2OoBwH8OwYGKW2QE_4Sep2013061732GMT_1378275452253.Ext
MD. Mohiuddin Ahmed
quelle
1
Dies garantiert keinen eindeutigen Dateinamen. Sie können diese Antwort jedoch erweitern, indem Sie überprüfen, ob die Datei vorhanden ist, und in diesem Fall eine weitere zufällige Zeichenfolge generieren.
Caleb Adams
Danke @CalebAdams :) Dies kann wahrscheinlich bei Multithreads passieren. Aktualisiert.
MD. Mohiuddin Ahmed
7

Schauen Sie sich das Datei-Javadoc an . Die Methode createNewFile erstellt die Datei nur, wenn sie nicht vorhanden ist, und gibt einen Booleschen Wert zurück, um anzugeben, ob die Datei erstellt wurde.

Sie können auch die Methode includes () verwenden:

int i = 0;
String filename = Integer.toString(i);
File f = new File(filename);
while (f.exists()) {
    i++;
    filename = Integer.toString(i);
    f = new File(filename);
}
f.createNewFile();
System.out.println("File in use: " + f);
pgras
quelle
Ich weiß nicht, ob ich darüber nachdenke. Was passiert, wenn f nach der while-Schleife und vor dem Aufruf von f.createNewFile () von einem anderen Prozess erstellt wird?
Guangliang
4

Wenn Sie Zugriff auf eine Datenbank haben, können Sie eine Sequenz im Dateinamen erstellen und verwenden.

select mySequence.nextval from dual;

Es ist garantiert einzigartig und sollte nicht zu groß werden (es sei denn, Sie pumpen eine Menge Dateien aus).

Shane
quelle
2
Warum um alles in der Welt wird das herabgestuft? Obwohl dies eindeutig nicht die eleganteste Lösung sein wird, sollte es zumindest die Anforderungen des OP erfüllen. Ich denke, es ist ein völlig gültiger Ansatz, den man in Betracht ziehen sollte, insbesondere wenn OP plant, diese Informationen irgendwie in eine Datenbank aufzunehmen.
Priidu Neemre
3
    //Generating Unique File Name
    public String getFileName() {
        String timeStamp = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss").format(new Date());
        return "PNG_" + timeStamp + "_.png";
    }
LEGENDE MORTAL
quelle
1
Die obige Methode garantiert nicht, dass zwei Schnellaufrufe (oder zwei verschiedene Threads gleichzeitig) eindeutige Ergebnisse liefern.
usr-local-ΕΨΗΕΛΩΝ
@ usr-local-ΕΨΗΕΛΩΝ Sie können auch Benutzer-ID anhängen, so dass dieser Dateiname eindeutig wäre und abt Thread können wir rxJava dafür verwenden
LEGEND MORTAL
Entschuldigung, ich muss stark widersprechen. Zwei Threads desselben Benutzers stoßen zusammen. Für Ihre API müssen Implementierer die Benutzer-ID erfassen. Java ist bekannt dafür, portabel zu sein. Was passiert, wenn Ihre Stimme auf einem benutzerlosen IoT-Gerät ausgeführt wird? Der richtige Weg besteht darin, einen for-Zyklus durchzuführen, bei dem ein zunehmendes Suffix angehängt wird, bis keine Datei mit diesem Suffix gefunden wird, und diese Datei sofort zu erstellen. Dies ist das gleiche File.createTempFileunter der Haube
usr-local-ΕΨΗΕΛΩΝ
2

Wenn Sie andere Antworten kombinieren, verwenden Sie den ms-Zeitstempel mit einem angehängten Zufallswert. Wiederholen, bis kein Konflikt mehr vorliegt, was in der Praxis so gut wie nie der Fall sein wird.

Zum Beispiel: Datei-ccyymmdd-hhmmss-mmm-rrrrrr.txt

Lawrence Dol
quelle
1

Warum nicht einfach etwas verwenden, das auf einem Zeitstempel basiert?

Galwegisch
quelle
1
Was ist, wenn zwei Dateien in derselben Millisekunde erstellt werden?
Jeff Bloom
2
Wiederholen Sie den Fehler, der neue Zeitstempel wird dann anders sein
JRL
2
@ Jeff. Erkennen Sie einfach den Konflikt und versuchen Sie es erneut, bis kein Konflikt mehr vorliegt. in der Praxis sollte dies sehr selten sein.
Lawrence Dol
1
Wenn Sie den Konflikt trotzdem erkennen möchten, generieren Sie einfach einen zufälligen Dateinamen, ohne sich Gedanken über die Zeit zu machen - siehe zum Beispiel meine Antwort. Es wird immer noch ziemlich selten sein, dass Sie den gleichen Dateinamen mit (sagen wir) 8 Zeichen in :) generieren
Jon Skeet
1

Problem ist die Synchronisation. Konfliktregionen trennen.

Benennen Sie die Datei wie folgt: (server-name)_(thread/process-name)_(millisecond/timestamp).(extension)
Beispiel:aws1_t1_1447402821007.png

Thakkar schlagen
quelle
0

Wie wäre es mit einer Generierung basierend auf einem auf die nächste Millisekunde gerundeten Zeitstempel oder einer beliebigen Genauigkeit, die Sie benötigen? Verwenden Sie dann eine Sperre, um den Zugriff auf die Funktion zu synchronisieren.

Wenn Sie den zuletzt generierten Dateinamen speichern, können Sie nach Bedarf fortlaufende Buchstaben oder weitere Ziffern anhängen, um ihn eindeutig zu machen.

Wenn Sie dies lieber ohne Sperren tun möchten, verwenden Sie einen Zeitschritt plus eine Thread-ID und stellen Sie sicher, dass die Funktion länger als eine Millisekunde dauert, oder warten Sie, bis dies der Fall ist.

justinhj
quelle
Die Verwendung der Sperrsynchronisation für solche Dinge ist fast immer eine schreckliche Idee - Mutexe schützen den internen Speicher des Programms und keine externen Ressourcen (z. B. Datenbank, Dateisystem).
MK.
0

Es sieht so aus, als hätten Sie eine Handvoll Lösungen zum Erstellen eines eindeutigen Dateinamens, also lasse ich das in Ruhe. Ich würde den Dateinamen folgendermaßen testen:

    String filePath;
    boolean fileNotFound = true;
    while (fileNotFound) {
        String testPath = generateFilename();

        try {
            RandomAccessFile f = new RandomAccessFile(
                new File(testPath), "r");
        } catch (Exception e) {
            // exception thrown by RandomAccessFile if 
            // testPath doesn't exist (ie: it can't be read)

            filePath = testPath;
            fileNotFound = false;
        }
    }
    //now create your file with filePath
Peter Anthony
quelle
0

Das funktioniert auch

String logFileName = new SimpleDateFormat("yyyyMMddHHmm'.txt'").format(new Date());

logFileName = "loggerFile_" + logFileName;
Akshay Prabhakar
quelle
0

Ich verstehe, dass ich zu spät bin, um auf diese Frage zu antworten. Aber ich denke, ich sollte das sagen, da es etwas anderes zu sein scheint als andere Lösungen.

Wir können den Threadnamen und den aktuellen Zeitstempel als Dateinamen verketten. Es gibt jedoch ein Problem, bei dem ein Thread-Name ein Sonderzeichen wie "\" enthält, das Probleme beim Erstellen des Dateinamens verursachen kann. So können wir spezielle Zeichen aus dem Threadnamen entfernen und dann den Threadnamen und den Zeitstempel verketten

fileName = threadName(after removing special charater) + currentTimeStamp
Roshan
quelle
0

Warum nicht synchronisiert verwenden, um Multi-Thread zu verarbeiten. Hier ist meine Lösung: Sie kann einen kurzen Dateinamen generieren und ist einzigartig.

private static synchronized String generateFileName(){
    String name = make(index);
    index ++;
    return name;
}
private static String make(int index) {
    if(index == 0) return "";
    return String.valueOf(chars[index % chars.length]) + make(index / chars.length);
}
private static int index = 1;
private static char[] chars = {'a','b','c','d','e','f','g',
        'h','i','j','k','l','m','n',
        'o','p','q','r','s','t',
        'u','v','w','x','y','z'};

geblasen ist die Hauptfunktion für den Test, es ist Arbeit.

public static void main(String[] args) {
    List<String> names = new ArrayList<>();
    List<Thread> threads = new ArrayList<>();
    for (int i = 0; i < 100; i++) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    String name = generateFileName();
                    names.add(name);
                }
            }
        });
        thread.run();
        threads.add(thread);
    }

    for (int i = 0; i < 10; i++) {
        try {
            threads.get(i).join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    System.out.println(names);
    System.out.println(names.size());

}
Ignorieren
quelle
0

Ich benutze aktuelle Millisekunden mit Zufallszahlen

dh

Random random=new Random();
String ext = ".jpeg";
File dir = new File("/home/pregzt");
String name = String.format("%s%s",System.currentTimeMillis(),random.nextInt(100000)+ext);
File file = new File(dir, name);
BALS
quelle