Frage

Gibt es eine Standard und zuverlässige Art und Weise ein temporäres Verzeichnis innerhalb einer Java-Anwendung zu schaffen? Es gibt einen Eintrag in Java Problem Datenbank , die ein Stück Code hat in Kommentare, aber ich frage mich, ob es eine Standardlösung ist in einem der üblichen Bibliotheken (Apache Commons etc.)?

zu finden
War es hilfreich?

Lösung

Wenn Sie mit JDK 7 Verwendung der neuen Files.createTempDirectory Klasse das temporäre Verzeichnis zu erstellen.

Path tempDirWithPrefix = Files.createTempDirectory(prefix);

Vor JDK 7 sollte es tun:

public static File createTempDirectory()
    throws IOException
{
    final File temp;

    temp = File.createTempFile("temp", Long.toString(System.nanoTime()));

    if(!(temp.delete()))
    {
        throw new IOException("Could not delete temp file: " + temp.getAbsolutePath());
    }

    if(!(temp.mkdir()))
    {
        throw new IOException("Could not create temp directory: " + temp.getAbsolutePath());
    }

    return (temp);
}

Sie könnten besser Ausnahmen (Unterklasse IOException) machen, wenn Sie wollen.

Andere Tipps

Die Google Guava Bibliothek hat eine Tonne von hilfreichen Utilities. Einer der Anmerkung ist hier die Dateien Klasse . Es hat eine Reihe von nützlichen Methoden, einschließlich:

File myTempDir = Files.createTempDir();

Das ist genau das, was Sie in einer Zeile gefragt. Wenn Sie die Dokumentation

Wenn Sie ein temporäres Verzeichnis für die Prüfung benötigen und Sie verwenden jUnit, @Rule zusammen mit TemporaryFolder löst Ihr Problem:

@Rule
public TemporaryFolder folder = new TemporaryFolder();

Von der Dokumentation :

  

Die TemporaryFolder Regel ermöglicht die Erstellung von Dateien und Ordnern, die garantiert werden gelöscht werden, wenn die Testverfahren beendet (ob es geht oder nicht)


Update:

Wenn Sie JUnit Jupiter verwenden (Version 5.1.1 oder höher), haben Sie die Option JUnit Pioneer zu verwenden, die die JUnit 5 Extension Pack ist.

Kopiert von der Projektdokumentation rel="nofollow:

  

Zum Beispiel der folgende Test registriert die Erweiterung für eine einzige Testmethode erstellt und schreibt eine Datei in das temporäre Verzeichnis und überprüft deren Inhalt.

@Test
@ExtendWith(TempDirectory.class)
void test(@TempDir Path tempDir) {
    Path file = tempDir.resolve("test.txt");
    writeFile(file);
    assertExpectedFileContent(file);
}

Weitere Informationen im JavaDoc und die JavaDoc von TempDirectory

Gradle:

dependencies {
    testImplementation 'org.junit-pioneer:junit-pioneer:0.1.2'
}

Maven:

<dependency>
   <groupId>org.junit-pioneer</groupId>
   <artifactId>junit-pioneer</artifactId>
   <version>0.1.2</version>
   <scope>test</scope>
</dependency>

Update 2:

Die @TempDir Anmerkung wurde die JUnit Jupiter 5.4.0 Release als experimentelles Feature hinzugefügt. Beispiel aus dem JUnit 5 Benutzerhandbuch :

@Test
void writeItemsToFile(@TempDir Path tempDir) throws IOException {
    Path file = tempDir.resolve("test.txt");

    new ListWriter(file).write("a", "b", "c");

    assertEquals(singletonList("a,b,c"), Files.readAllLines(file));
}

Naiv geschrieben Code, um dieses Problem zu lösen, leidet unter Rennbedingungen, darunter mehrere der Antworten hier. Historisch gesehen könnte man sorgfältig über Rennbedingungen denken und es selbst schreiben, oder Sie können eine Drittanbieter-Bibliothek wie Googles Guava verwenden (wie Spina Antwort vorgeschlagen.) Oder Sie könnten fehlerhaften Code schreiben.

Aber ab JDK 7, gibt es gute Nachrichten! Die Java-Standard-Bibliothek selbst bietet jetzt eine ordnungsgemäß funktionierende (nicht-rassig) Lösung für dieses Problem. Sie wollen java.nio.file.Files # createTempDirectory () . Von der Dokumentation :

public static Path createTempDirectory(Path dir,
                       String prefix,
                       FileAttribute<?>... attrs)
                                throws IOException
  

Erstellt ein neues Verzeichnis im angegebenen Verzeichnis, das gegebenen Präfix seinen Namen zu generieren. Der resultierende Pfad wird mit dem gleichen Dateisystem wie das angegebene Verzeichnis zugeordnet ist.

     

Die Details, wie der Name des Verzeichnisses, aufgebaut ist, ist die Umsetzung abhängig und daher nicht angegeben. Wo möglich, wird das Präfix verwendet Kandidatennamen zu konstruieren.

Dies löst effektiv die in der Sun Bug rel="noreferrer"> tracker, die für eine solche Funktion gefragt.

Verwenden Sie keine deleteOnExit() auch wenn Sie es ausdrücklich später löschen.

Google 'deleteonexit ist böse' für weitere Informationen, aber die Kern des Problems ist:

  1. deleteOnExit() löscht nur für normales JVM Abschaltungen, nicht abstürzt oder den JVM-Prozess zu töten.

  2. deleteOnExit() löscht nur auf JVM Shutdown - nicht gut für lange laufenden Serverprozess, weil:

  3. Die bösesten aller - deleteOnExit() verbraucht für jede temporäre Datei Eingabespeicher. Wenn Ihr Prozess für Monate ausgeführt wird, oder erstellt eine Menge von temporären Dateien in kurzer Zeit verbrauchen Sie Gedächtnis und es nie loslassen, bis die JVM heruntergefahren.

Wie von Java 1.7 createTempDirectory(prefix, attrs) und createTempDirectory(dir, prefix, attrs) sind in java.nio.file.Files enthalten

Beispiel: File tempDir = Files.createTempDirectory("foobar").toFile();

Das ist das, was ich für meinen eigenen Code zu tun beschlossen:

/**
 * Create a new temporary directory. Use something like
 * {@link #recursiveDelete(File)} to clean this directory up since it isn't
 * deleted automatically
 * @return  the new directory
 * @throws IOException if there is an error creating the temporary directory
 */
public static File createTempDir() throws IOException
{
    final File sysTempDir = new File(System.getProperty("java.io.tmpdir"));
    File newTempDir;
    final int maxAttempts = 9;
    int attemptCount = 0;
    do
    {
        attemptCount++;
        if(attemptCount > maxAttempts)
        {
            throw new IOException(
                    "The highly improbable has occurred! Failed to " +
                    "create a unique temporary directory after " +
                    maxAttempts + " attempts.");
        }
        String dirName = UUID.randomUUID().toString();
        newTempDir = new File(sysTempDir, dirName);
    } while(newTempDir.exists());

    if(newTempDir.mkdirs())
    {
        return newTempDir;
    }
    else
    {
        throw new IOException(
                "Failed to create temp dir named " +
                newTempDir.getAbsolutePath());
    }
}

/**
 * Recursively delete file or directory
 * @param fileOrDir
 *          the file or dir to delete
 * @return
 *          true iff all files are successfully deleted
 */
public static boolean recursiveDelete(File fileOrDir)
{
    if(fileOrDir.isDirectory())
    {
        // recursively delete contents
        for(File innerFile: fileOrDir.listFiles())
        {
            if(!FileUtilities.recursiveDelete(innerFile))
            {
                return false;
            }
        }
    }

    return fileOrDir.delete();
}

Nun, "createTempFile" erstellt die Datei tatsächlich. Also, warum es nicht nur zuerst löschen, und führen Sie dann die mkdir auf sie?

Dieser Code sollte funktionieren recht gut:

public static File createTempDir() {
    final String baseTempPath = System.getProperty("java.io.tmpdir");

    Random rand = new Random();
    int randomInt = 1 + rand.nextInt();

    File tempDir = new File(baseTempPath + File.separator + "tempDir" + randomInt);
    if (tempDir.exists() == false) {
        tempDir.mkdir();
    }

    tempDir.deleteOnExit();

    return tempDir;
}

diskutiert Wie in dieser RFE und seine Kommentare, könnten Sie rufen tempDir.delete() zuerst. Oder Sie könnten System.getProperty("java.io.tmpdir") verwenden und ein Verzeichnis dort erstellen. So oder so, sollten Sie daran denken tempDir.deleteOnExit() zu nennen, oder die Datei wird nicht gelöscht, nachdem Sie fertig sind.

Nur für den Abschluss, das ist der Code von Google Guave Bibliothek. Es ist nicht mein Code, aber ich denke, es ist wertvoll ist es in diesem Thread hier zu zeigen.

  /** Maximum loop count when creating temp directories. */
  private static final int TEMP_DIR_ATTEMPTS = 10000;

  /**
   * Atomically creates a new directory somewhere beneath the system's temporary directory (as
   * defined by the {@code java.io.tmpdir} system property), and returns its name.
   *
   * <p>Use this method instead of {@link File#createTempFile(String, String)} when you wish to
   * create a directory, not a regular file. A common pitfall is to call {@code createTempFile},
   * delete the file and create a directory in its place, but this leads a race condition which can
   * be exploited to create security vulnerabilities, especially when executable files are to be
   * written into the directory.
   *
   * <p>This method assumes that the temporary volume is writable, has free inodes and free blocks,
   * and that it will not be called thousands of times per second.
   *
   * @return the newly-created directory
   * @throws IllegalStateException if the directory could not be created
   */
  public static File createTempDir() {
    File baseDir = new File(System.getProperty("java.io.tmpdir"));
    String baseName = System.currentTimeMillis() + "-";

    for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) {
      File tempDir = new File(baseDir, baseName + counter);
      if (tempDir.mkdir()) {
        return tempDir;
      }
    }
    throw new IllegalStateException(
        "Failed to create directory within "
            + TEMP_DIR_ATTEMPTS
            + " attempts (tried "
            + baseName
            + "0 to "
            + baseName
            + (TEMP_DIR_ATTEMPTS - 1)
            + ')');
  }

habe ich das gleiche Problem so dass diese nur eine andere Antwort für diejenigen, die interessiert sind, und es ist ähnlich wie eine der oben:

public static final String tempDir = System.getProperty("java.io.tmpdir")+"tmp"+System.nanoTime();
static {
    File f = new File(tempDir);
    if(!f.exists())
        f.mkdir();
}

Und für meine Anwendung, habe ich beschlossen, dass in einer Option hinzufügen, um die Temp löschen an der Ausfahrt, damit ich in einem Shut-down Haken hinzugefügt:

Runtime.getRuntime().addShutdownHook(new Thread() {
        @Override
        public void run() {
            //stackless deletion
            String root = MainWindow.tempDir;
            Stack<String> dirStack = new Stack<String>();
            dirStack.push(root);
            while(!dirStack.empty()) {
                String dir = dirStack.pop();
                File f = new File(dir);
                if(f.listFiles().length==0)
                    f.delete();
                else {
                    dirStack.push(dir);
                    for(File ff: f.listFiles()) {
                        if(ff.isFile())
                            ff.delete();
                        else if(ff.isDirectory())
                            dirStack.push(ff.getPath());
                    }
                }
            }
        }
    });

Die Methode alle subdirs und Dateien löschen, bevor Sie den Temp Löschen , ohne die Aufrufhierarchie zu verwenden (was völlig optional ist und man könnte es mit Rekursion an dieser Stelle tun), aber ich möchte auf das sein, sichere Seite.

Wie Sie in den anderen Antworten sehen, hat kein einheitlicher Ansatz entstanden. Daher bereits Sie Apache Commons erwähnt, schlage ich den folgenden Ansatz FileUtils von Apache Commons IO :

/**
 * Creates a temporary subdirectory in the standard temporary directory.
 * This will be automatically deleted upon exit.
 * 
 * @param prefix
 *            the prefix used to create the directory, completed by a
 *            current timestamp. Use for instance your application's name
 * @return the directory
 */
public static File createTempDirectory(String prefix) {

    final File tmp = new File(FileUtils.getTempDirectory().getAbsolutePath()
            + "/" + prefix + System.currentTimeMillis());
    tmp.mkdir();
    Runtime.getRuntime().addShutdownHook(new Thread() {

        @Override
        public void run() {

            try {
                FileUtils.deleteDirectory(tmp);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    });
    return tmp;

}

Dies ist seit Apache Commons die Bibliothek bevorzugt, die als am nächsten zu der Frage „Standard“ kommt und funktionieren sowohl mit JDK 7 und älteren Versionen. Dies gibt auch eine „alte“ File-Instanz (die Strom basiert) und nicht eine „neue“ Path-Instanz (die Puffer basiert und würde das Ergebnis JDK7 des getTemporaryDirectory () Methode) -> Daher gibt es, was die meisten Menschen brauchen, wenn sie wollen ein temporäres Verzeichnis erstellen.

Ich mag die viele Versuche, einen eindeutigen Namen zu schaffen, sondern auch diese Lösung nicht aus, eine Race-Bedingung. Ein anderes Verfahren kann nach dem Test für exists() einschleichen und den if(newTempDir.mkdirs()) Methodenaufruf. Ich habe keine Ahnung, wie man völlig diese sicher zu machen, ohne zu nativem Code zurückgreifen, die ich nehme an ist, was in File.createTempFile() begraben wird.

Vor Java 7 könnten Sie auch:

File folder = File.createTempFile("testFileUtils", ""); // no suffix
folder.delete();
folder.mkdirs();
folder.deleteOnExit();

Mit File#createTempFile und delete einen eindeutigen Namen erstellen für das Verzeichnis ok scheint. Sie sollten einen ShutdownHook fügen Sie das Verzeichnis (rekursiv) auf JVM Shutdown zu löschen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top