Was ist der beste Weg, um das Home-Verzeichnis des Benutzers in Java zu finden?

279

Die Schwierigkeit besteht darin, dass es plattformübergreifend sein sollte. Windows 2000, XP, Vista, OSX, Linux, andere Unix-Varianten. Ich suche nach einem Codeausschnitt, der dies für alle Plattformen erreichen kann, und nach einer Möglichkeit, die Plattform zu erkennen.

Jetzt sollten Sie sich des Fehlers 4787931 bewusst sein , user.homeder nicht richtig funktioniert. Geben Sie mir daher bitte keine Antworten aus dem Lehrbuch. Diese finden Sie selbst in den Handbüchern.

Bruno Ranschaert
quelle
1
Haben Sie die im Fehler genannten Problemumgehungen ausprobiert? Es gibt viele Vorschläge.
Joachim Sauer
1
Fehler 4787931 für Java-Versionen bis 1.4.2 wird erneut als Fehler 6519127 für Java 1.6 angezeigt. Das Problem verschwindet nicht und wird immer noch als niedrige Priorität aufgeführt.
GregA100k
16
Hinweis: Fehler 4787391 ist in Java 8 als behoben markiert
Steven R. Loomis

Antworten:

364

Der Fehler, auf den Sie verweisen (Fehler 4787391), wurde in Java 8 behoben. Selbst wenn Sie eine ältere Version von Java verwenden, ist der System.getProperty("user.home")Ansatz wahrscheinlich immer noch der beste. Der user.homeAnsatz scheint in sehr vielen Fällen zu funktionieren. Eine 100% kugelsichere Lösung unter Windows ist schwierig, da Windows ein sich änderndes Konzept der Bedeutung des Home-Verzeichnisses hat.

Wenn user.homees Ihnen nicht gut genug ist, würde ich vorschlagen, eine Definition von home directoryfür Windows auszuwählen und diese zu verwenden und die entsprechende Umgebungsvariable mit zu erhalten System.getenv(String).

DJClayworth
quelle
135

Mit Java 8 ist der richtige Weg:

System.getProperty("user.home");

Der Fehler JDK-6519127 wurde behoben und die „Inkompatibilität zwischen JDK 8 und JDK 7“ Abschnitt der Release Notes heißt es :

Bereich: Core Libs / java.lang

Zusammenfassung

Die Schritte zum Ermitteln des Basisverzeichnisses des Benutzers unter Windows wurden geändert, um dem von Microsoft empfohlenen Ansatz zu folgen. Diese Änderung kann in älteren Windows-Editionen oder dort beobachtet werden, wo Registrierungseinstellungen oder Umgebungsvariablen auf andere Verzeichnisse festgelegt sind. Art der Inkompatibilität

behavioral RFE

6519127

Obwohl die Frage alt ist, lasse ich dies zum späteren Nachschlagen.

Paulo Fidalgo
quelle
35
System.getProperty("user.home");

Siehe JavaDoc .

Joachim Sauer
quelle
11
Nein, keine richtige Antwort, dies ist die gleiche wie oben. Ja, ich habe nicht nur die JavaDocs gelesen, sondern sie auch auf allen Plattformen ausprobiert, bevor ich diese Frage gestellt habe! Die Antwort ist nicht so einfach.
Bruno Ranschaert
3
Dies kann unter Windows schrecklich schief gehen, wo nur das übergeordnete Verzeichnis des Desktop-Verzeichnisses benötigt wird, das sich möglicherweise irgendwo befindet…
Chronial
29

Das Konzept eines HOME-Verzeichnisses scheint in Bezug auf Windows etwas vage zu sein. Wenn die Umgebungsvariablen (HOMEDRIVE / HOMEPATH / USERPROFILE) nicht ausreichen, müssen Sie möglicherweise auf native Funktionen über JNI oder JNA zurückgreifen . SHGetFolderPath können Sie spezielle Ordner abzurufen, wie My Documents (CSIDL_PERSONAL) oder Lokale Einstellungen \ Anwendungsdaten (CSIDL_LOCAL_APPDATA).

Beispiel-JNA-Code:

public class PrintAppDataDir {

    public static void main(String[] args) {
        if (com.sun.jna.Platform.isWindows()) {
            HWND hwndOwner = null;
            int nFolder = Shell32.CSIDL_LOCAL_APPDATA;
            HANDLE hToken = null;
            int dwFlags = Shell32.SHGFP_TYPE_CURRENT;
            char[] pszPath = new char[Shell32.MAX_PATH];
            int hResult = Shell32.INSTANCE.SHGetFolderPath(hwndOwner, nFolder,
                    hToken, dwFlags, pszPath);
            if (Shell32.S_OK == hResult) {
                String path = new String(pszPath);
                int len = path.indexOf('\0');
                path = path.substring(0, len);
                System.out.println(path);
            } else {
                System.err.println("Error: " + hResult);
            }
        }
    }

    private static Map<String, Object> OPTIONS = new HashMap<String, Object>();
    static {
        OPTIONS.put(Library.OPTION_TYPE_MAPPER, W32APITypeMapper.UNICODE);
        OPTIONS.put(Library.OPTION_FUNCTION_MAPPER,
                W32APIFunctionMapper.UNICODE);
    }

    static class HANDLE extends PointerType implements NativeMapped {
    }

    static class HWND extends HANDLE {
    }

    static interface Shell32 extends Library {

        public static final int MAX_PATH = 260;
        public static final int CSIDL_LOCAL_APPDATA = 0x001c;
        public static final int SHGFP_TYPE_CURRENT = 0;
        public static final int SHGFP_TYPE_DEFAULT = 1;
        public static final int S_OK = 0;

        static Shell32 INSTANCE = (Shell32) Native.loadLibrary("shell32",
                Shell32.class, OPTIONS);

        /**
         * see http://msdn.microsoft.com/en-us/library/bb762181(VS.85).aspx
         * 
         * HRESULT SHGetFolderPath( HWND hwndOwner, int nFolder, HANDLE hToken,
         * DWORD dwFlags, LPTSTR pszPath);
         */
        public int SHGetFolderPath(HWND hwndOwner, int nFolder, HANDLE hToken,
                int dwFlags, char[] pszPath);

    }

}
McDowell
quelle
Zu Ihrer Information, der Ordner, der dem Home-Verzeichnis des Benutzers entspricht, ist CSIDL_PROFILE. Siehe msdn.microsoft.com/en-us/library/bb762494(VS.85).aspx .
Matt Solnit
Ja, dies ist eine aufwändige Version für den Windows-Fall.
Bruno Ranschaert
2
In neueren Versionen von JNA (genauer gesagt jna-platform) gibt es eine Shell32Util-Klasse, die die entsprechende Windows-API auf sehr schöne Weise kapselt. Insbesondere sollte die Verwendung von Shell32Util.getKnownFolderPath (...) in Kombination mit einer der Konstanten aus der KnownFolders-Klasse angemessen sein. Die ältere getFolderPath-API-Funktion ist seit Windows Vista veraltet.
Sebastian Marsching
17

Andere haben die Frage vor mir beantwortet, aber ein nützliches Programm zum Ausdrucken aller verfügbaren Eigenschaften ist:

for (Map.Entry<?,?> e : System.getProperties().entrySet()) {
    System.out.println(String.format("%s = %s", e.getKey(), e.getValue())); 
}
oxbow_lakes
quelle
Ich würde mich nicht darauf verlassen, da nicht alle Eigenschaften standardisiert sind. Überprüfen Sie stattdessen JavaDoc für System.getProperties (), um herauszufinden, welche Eigenschaften garantiert vorhanden sind.
Joachim Sauer
6
Das mag wahr sein, aber es ist immer noch ziemlich nützlich für einen Neuling, denke ich! Ich bin nicht sicher, ob es 2 Downvotes verdient :-(
oxbow_lakes
6

Als ich nach der Scala-Version suchte, konnte ich oben nur McDowells JNA-Code finden. Ich füge hier meinen Scala-Port hinzu, da es derzeit keinen angemesseneren gibt.

import com.sun.jna.platform.win32._
object jna {
    def getHome: java.io.File = {
        if (!com.sun.jna.Platform.isWindows()) {
            new java.io.File(System.getProperty("user.home"))
        }
        else {
            val pszPath: Array[Char] = new Array[Char](WinDef.MAX_PATH)
            new java.io.File(Shell32.INSTANCE.SHGetSpecialFolderPath(null, pszPath, ShlObj.CSIDL_MYDOCUMENTS, false) match {
                case true => new String(pszPath.takeWhile(c => c != '\0'))
                case _    => System.getProperty("user.home")
            })
        }
    }
}

Wie bei der Java-Version müssen Sie Ihren referenzierten Bibliotheken Java Native Access , einschließlich beider JAR-Dateien, hinzufügen .

Es ist schön zu sehen, dass JNA dies jetzt viel einfacher macht als zu dem Zeitpunkt, als der ursprüngliche Code veröffentlicht wurde.

Peter
quelle
2

Ich würde den im Fehlerbericht beschriebenen Algorithmus mit System.getenv (String) verwenden und auf die Verwendung der Eigenschaft user.dir zurückgreifen, wenn keine der Umgebungsvariablen ein gültiges vorhandenes Verzeichnis angibt. Dies sollte plattformübergreifend funktionieren.

Ich denke, unter Windows ist das fiktive "Dokumente" -Verzeichnis des Benutzers das, wonach Sie wirklich suchen.

Lawrence Dol
quelle
2

Alternativ wäre die Verwendung von Apache CommonsIO FileUtils.getUserDirectory()anstelle von System.getProperty("user.home"). Sie erhalten das gleiche Ergebnis und es besteht keine Möglichkeit, einen Tippfehler einzuführen, wenn Sie die Systemeigenschaft angeben.

Es besteht eine große Chance, dass Sie bereits eine Apache CommonsIO-Bibliothek in Ihrem Projekt haben. Führen Sie es nicht ein, wenn Sie es nur zum Abrufen des Benutzer-Home-Verzeichnisses verwenden möchten.

mladzo
quelle
0

Wenn Sie etwas möchten, das unter Windows gut funktioniert, gibt es ein Paket namens WinFoldersJava, das den nativen Aufruf umschließt , um die "speziellen" Verzeichnisse unter Windows abzurufen . Wir benutzen es häufig und es funktioniert gut.

Neil Benn
quelle