Jaki jest najlepszy sposób na znalezienie katalogu domowego użytkowników w Javie?

279

Trudność polega na tym, że powinna to być platforma. Windows 2000, XP, Vista, OSX, Linux, inne warianty unix. Szukam fragmentu kodu, który może to zrobić dla wszystkich platform, i sposobu na wykrycie platformy.

Teraz powinieneś zdawać sobie sprawę z błędu 4787931 , user.homektóry nie działa poprawnie, więc proszę nie dostarczaj mi odpowiedzi z podręcznika, znajdę je sam w instrukcjach.

Bruno Ranschaert
źródło
1
Czy wypróbowałeś obejścia wspomniane w błędzie? Istnieje wiele sugestii.
Joachim Sauer
1
błąd 4787931 dla wersji java do 1.4.2 pojawia się ponownie jako błąd 6519127 dla java 1.6. Problem nie zniknie i nadal jest wymieniony jako niski priorytet.
GregA100k
16
Uwaga: błąd 4787391 jest oznaczony jako naprawiony w Javie 8
Steven R. Loomis

Odpowiedzi:

364

Błąd, do którego się odwołujesz (błąd 4787391) został naprawiony w Javie 8. Nawet jeśli używasz starszej wersji Javy, to System.getProperty("user.home")podejście jest prawdopodobnie najlepsze. user.homePodejście wydaje się działać w bardzo dużej liczbie przypadków. W 100% kuloodporne rozwiązanie w systemie Windows jest trudne, ponieważ system Windows zmienia koncepcję tego, co oznacza katalog domowy.

Jeśli user.homenie jest dla ciebie wystarczająco dobry, sugerowałbym wybranie definicji systemu home directoryWindows i użycie jej, uzyskanie odpowiedniej zmiennej środowiskowej za pomocą System.getenv(String).

DJClayworth
źródło
135

Właściwie w Javie 8 właściwym sposobem jest użycie:

System.getProperty("user.home");

Błąd JDK-6519127 został naprawiony, a sekcja „Niezgodności między JDK 8 i JDK 7” informacji o wydaniu brzmi:

Obszar: Core Libs / java.lang

Streszczenie

Kroki użyte do ustalenia katalogu domowego użytkownika w systemie Windows zostały zmienione, aby zastosować podejście zalecane przez Microsoft. Ta zmiana może być obserwowana w starszych wersjach systemu Windows lub w ustawieniach rejestru lub zmiennych środowiskowych ustawionych na inne katalogi. Natura niezgodności

behavioral RFE

6519127

Pomimo tego, że pytanie jest stare, pozostawiam to na przyszłość.

Paulo Fidalgo
źródło
35
System.getProperty("user.home");

Zobacz JavaDoc .

Joachim Sauer
źródło
11
Nie, nieprawidłowa odpowiedź, to ta sama jak powyżej. Tak, nie tylko przeczytałem JavaDocs, ale także wypróbowałem go na wszystkich platformach, zanim zadałem to pytanie! Odpowiedź nie jest taka prosta.
Bruno Ranschaert
3
To może pójść okropnie źle w systemie Windows, gdzie po prostu zabierze nadrzędny katalog pulpitu, który może być gdziekolwiek…
Chronial 12.12
29

Koncepcja katalogu HOME wydaje się nieco niejasna, jeśli chodzi o system Windows. Jeśli… zmienne środowiskowe (HOMEDRIVE / HOMEPATH / USERPROFILE) nie są wystarczające, być może będziesz musiał skorzystać z funkcji rodzimych za pośrednictwem JNI lub JNA . SHGetFolderPath umożliwia pobieranie specjalnych folderów, takich jak Moje dokumenty (CSIDL_PERSONAL) lub Ustawienia lokalne \ Dane aplikacji (CSIDL_LOCAL_APPDATA).

Przykładowy kod JNA:

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
źródło
Do Twojej wiadomości, folder odpowiadający katalogowi osobistemu użytkownika to CSIDL_PROFILE. Zobacz msdn.microsoft.com/en-us/library/bb762494(VS.85).aspx .
Matt Solnit,
Tak, jest to rozbudowana wersja dla systemu Windows.
Bruno Ranschaert
2
W najnowszych wersjach JNA (a dokładniej jna-platform) istnieje klasa Shell32Util, która bardzo ładnie obudowuje odpowiedni interfejs Windows API. W szczególności właściwe powinno być użycie Shell32Util.getKnownFolderPath (...) w połączeniu z jedną ze stałych z klasy FamousFolders. Starsza funkcja API getFolderPath jest przestarzała od Windows Vista.
Sebastian Marsching
17

Inni odpowiedzieli na pytanie przede mną, ale przydatnym programem do wydrukowania wszystkich dostępnych właściwości jest:

for (Map.Entry<?,?> e : System.getProperties().entrySet()) {
    System.out.println(String.format("%s = %s", e.getKey(), e.getValue())); 
}
oxbow_lakes
źródło
Nie zależałbym od tego, ponieważ nie wszystkie właściwości są znormalizowane. Zamiast tego sprawdź JavaDoc dla System.getProperties (), aby dowiedzieć się, które właściwości są gwarantowane.
Joachim Sauer
6
To może być prawda, ale wydaje mi się, że jest to całkiem przydatne dla początkującego! Nie jestem pewien, czy zasługuje na 2 downvotes :-(
oxbow_lakes
6

Gdy szukałem wersji Scali, wszystko, co mogłem znaleźć, to powyższy kod JD McDowell. Podaję tutaj mój port Scala, ponieważ obecnie nie ma nigdzie bardziej odpowiedniego.

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")
            })
        }
    }
}

Podobnie jak w przypadku wersji Java, musisz dodać Java Native Access , w tym oba pliki jar, do bibliotek, do których się odwołujesz.

Miło jest widzieć, że JNA sprawia, że ​​jest to o wiele łatwiejsze niż po opublikowaniu oryginalnego kodu.

Piotr
źródło
2

Użyłbym algorytmu opisanego w raporcie o błędzie przy użyciu System.getenv (String) i powróciłbym do korzystania z właściwości user.dir, jeśli żadna ze zmiennych środowiskowych nie wskazywała na poprawny istniejący katalog. To powinno działać na różnych platformach.

Wydaje mi się, że pod Windows naprawdę szukasz katalogu „dokumentów” użytkownika.

Lawrence Dol
źródło
2

Alternatywą byłoby użycie Apache CommonsIO FileUtils.getUserDirectory()zamiastSystem.getProperty("user.home") . Otrzymasz ten sam wynik i nie ma szans na literówkę podczas określania właściwości systemowej.

Istnieje duża szansa, że ​​masz już bibliotekę Apache CommonsIO w swoim projekcie. Nie wprowadzaj go, jeśli zamierzasz go używać tylko do uzyskiwania katalogu domowego użytkownika.

mladzo
źródło
0

Jeśli chcesz czegoś, co działa dobrze w systemie Windows, jest pakiet o nazwie WinFoldersJava, który otacza natywne wywołanie w celu uzyskania „specjalnych” katalogów w systemie Windows. Używamy go często i działa dobrze.

Neil Benn
źródło