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.


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).


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ść.


35
System.getProperty("user.home");

Zobacz JavaDoc .


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);

    }

}

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())); 
}

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.


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.


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.


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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.