13 Stimmen

Wie erhält man eine aussagekräftige Meldung für fehlgeschlagene Aufrufe von Java File Objekten (mkdir, rename, delete)

Bei der Verwendung von Datei.mkdir und Freunden fällt mir auf, dass sie bei Fehlschlägen keine Ausnahmen machen! Glücklicherweise hat FindBugs mich darauf hingewiesen, und jetzt prüft mein Code zumindest den Rückgabewert, aber ich sehe immer noch keine Möglichkeit, sinnvolle Informationen über warum der Anruf scheitert!

Wie kann ich herausfinden, warum Aufrufe dieser Dateimethoden fehlschlagen? Gibt es eine gute Alternative oder eine Bibliothek, die dies behandelt?

Ich habe einige Recherchen hier auf SO und Google durchgeführt und überraschend wenig Informationen zu diesem Thema gefunden.

[Update] Ich habe VFS ausprobiert und seine Ausnahmen haben keine nützlichen Informationen mehr. Der Versuch, ein Verzeichnis zu verschieben, das kürzlich gelöscht wurde, führte beispielsweise zu Could not rename file "D:\path\to\fileA" to "file:///D:/path/do/fileB". Kein Hinweis darauf, dass die Datei A nicht mehr existiert.

[update] Geschäftsanforderungen beschränken mich auf JDK 1.6 Lösungen, daher ist JDK 1.7 out

9voto

sbridges Punkte 24664

Sie könnten systemeigene Methoden aufrufen und auf diese Weise korrekte Fehlercodes erhalten. Zum Beispiel, die c-Funktion mkdir hat Fehlercodes wie EEXIST und ENOSPC. Sie können verwenden JNA auf diese nativen Funktionen zugreifen können. Wenn Sie *nix und Windows unterstützen, müssen Sie zwei Versionen dieses Codes erstellen.

Für ein Beispiel von jna mkdir unter Linux können Sie dies tun,

import java.io.IOException;

import com.sun.jna.LastErrorException;
import com.sun.jna.Native;

public class FileUtils {

  private static final int EACCES = 13;
  private static final int EEXIST = 17;
  private static final int EMLINK = 31;
  private static final int EROFS = 30;
  private static final int ENOSPC = 28;
  private static final int ENAMETOOLONG = 63;

  static void mkdir(String path) throws IOException {

    try {
      NativeLinkFileUtils.mkdir(path);

    } catch (LastErrorException e) {
      int errno = e.getErrorCode();
      if (errno == EACCES)
        throw new IOException(
            "Write permission is denied for the parent directory in which the new directory is to be added.");
      if (errno == EEXIST)
        throw new IOException("A file named " + path + " already exists.");
      if (errno == EMLINK)
        throw new IOException(
            "The parent directory has too many links (entries).  Well-designed file systems never report this error, because they permit more links than your disk could possibly hold. However, you must still take account of the possibility of this error, as it could result from network access to a file system on another machine.");
      if (errno == ENOSPC)
        throw new IOException(
            "The file system doesn't have enough room to create the new directory.");
      if (errno == EROFS)
        throw new IOException(
            "The parent directory of the directory being created is on a read-only file system and cannot be modified.");
      if (errno == EACCES)
        throw new IOException(
            "The process does not have search permission for a directory component of the file name.");
      if (errno == ENAMETOOLONG)
        throw new IOException(
            "This error is used when either the total length of a file name is greater than PATH_MAX, or when an individual file name component has a length greater than NAME_MAX. See section 31.6 Limits on File System Capacity.");
      else
        throw new IOException("unknown error:" + errno);
    }

  }
}

class NativeLinkFileUtils {
  static {
    try {
      Native.register("c");
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  static native int mkdir(String dir) throws LastErrorException;

}

5voto

Martijn Courteaux Punkte 65602

Sie können eine Hilfsklasse mit einem Inhalt wie diesem erstellen:

public int mkdir(File dirToCreate) throws IOException
{
    if (dirToCreate.exists())
        throw new IOException("Folder already exists");

    if (!dirToCreate.getParent().canWrite())
        throw new IOException("No write access to create the folder");

    return dirToCreate.mkdir();
}

public int rename(File from, File to) throws IOException, FileNotFoundException
{
    if (from.equals(to))
        throw new IllegalArgumentException("Files are equal");

    if (!from.exists())
        throw new FileNotFoundException(from.getAbsolutePath() + " is not found");

    if (!to.getParent().exists())
        throw new IllegalAccessException("Parent of the destination doesn't exist");

    if (!to.getParent().canWrite())
        throw new IllegalAccessException("No write access to move the file/folder");

    return from.renameTo(to);
}

Natürlich ist dies nicht vollständig, aber Sie können diese Idee ausarbeiten.

5voto

Ryan Stewart Punkte 120600

Verwenden Sie die JDK7 neue Datei-API . Es verfügt über eine viel bessere Betriebssystemintegration und bietet detailliertere Rückmeldungen. Siehe die Dokumente zum Verschieben/Umbenennen zum Beispiel.

3voto

Bohemian Punkte 386825

Sie könnten den Befehl ausführen und die err Ausgabe, die eine aussagekräftige Nachricht enthält.

Hier ist ein minimaler ausführbarer Code (der die apache commons-exec ), die zeigt, wie dies funktionieren könnte:

import org.apache.commons.exec.*;

public static String getErrorMessage(String command) {
    CommandLine cmdLine = CommandLine.parse(command);
    DefaultExecutor executor = new DefaultExecutor();
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    ByteArrayOutputStream err = new ByteArrayOutputStream();
    executor.setStreamHandler(new PumpStreamHandler(out, err));
    try {
        executor.execute(cmdLine);
    } catch (Exception e) {
        return err.toString().trim();
    }
    return null; // no error occurred
}

Hier ist ein Test dieses Codes, der eine Reihe von Fehlern bei der Dateioperation zeigt:

public static void main(String[] args) throws Exception {
    System.out.println(getErrorMessage("cp fake.file x"));
    System.out.println(getErrorMessage("cp /tmp /tmp"));
    System.out.println(getErrorMessage("mkdir /Volumes"));
    System.out.println(getErrorMessage("mv /tmp /"));
    System.out.println(getErrorMessage("mv fake.file /tmp"));
}

Ausgabe (unter Mac OSX):

cp: fake.file: No such file or directory
cp: /tmp is a directory (not copied).
mkdir: /Volumes: File exists
mv: /tmp and /tmp are identical
mv: rename fake.file to /tmp/fake.file: No such file or directory

Sie könnten die obige Methode in eine Methode verpacken, die eine IOException auslöst, die nach Erhalt der Nachricht diese nach den Schlüsselparametern analysiert und die Nachrichten mithilfe von Regex-Matching oder contains zu bestimmten IOExceptions und werfen sie, z.B.:

if (message.endsWith("No such file or directory"))
    throw new FileNotFoundException();  // Use IOExceptions if you can
if (message.endsWith("are identical"))
    throw new IdenticalFileException(); // Create your own Exceptions that extend IOException

Wenn Sie dies für die Verwendung auf mehreren Betriebssystemen abstrahieren wollten, müssten Sie Code für jede Plattform implementieren (Windows und *nix verwenden unterschiedliche Shell-Befehle/Fehlsmeldungen für eine bestimmte Dateioperation/ein bestimmtes Ergebnis).

Wenn die Prämie für diese Antwort vergeben wird, werde ich eine vollständige, aufgeräumte Version des funktionierenden Codes posten, einschließlich eines schicken enum für Dateioperationen.

1voto

AlexR Punkte 111534

Sie können stattdessen jakarta VFS verwenden. FileObject.createFolder() wirft FileSystemException die den Fehlercode enthält. Dies geschieht anstelle der Implementierung der Logik, die von @Martijn Courteaux

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X