Каков наилучший способ найти домашний каталог пользователей на Java?


280

Сложность в том, что он должен быть кроссплатформенным. Windows 2000, XP, Vista, OSX, Linux, другие варианты Unix. Я ищу фрагмент кода, который может выполнить это для всех платформ, и способ обнаружить платформу.

Теперь вы должны знать об ошибке 4787931 , user.homeкоторая не работает должным образом, поэтому, пожалуйста, не предоставляйте мне ответы в учебниках, я сам могу найти их в руководствах.


1
Вы пробовали обходные пути, упомянутые в ошибке? Есть много предложений.
— Йоахим Зауэр

1
ошибка 4787931 для версий Java до 1.4.2 снова появляется как ошибка 6519127 для Java 1.6. Проблема не исчезает и по-прежнему указывается как низкий приоритет.
— GregA100k

16
Примечание: ошибка 4787391 помечена как исправленная в Java 8
— Стивен Р. Лумис

Ответы:


366

Ошибка, на которую вы ссылаетесь (ошибка 4787391), была исправлена ​​в Java 8. Даже если вы используете более старую версию Java, этот System.getProperty("user.home")подход, вероятно, все еще остается лучшим. user.homeПодход , кажется, работает в очень большом количестве случаев. На 100% пуленепробиваемое решение для Windows сложно, потому что Windows имеет смещенное представление о том, что означает домашний каталог.

Если вам user.homeэто не подходит, я бы предложил выбрать определение home directoryдля окон и использовать его, получив соответствующую переменную окружения с помощью System.getenv(String).


136

На самом деле с Java 8 правильный путь заключается в использовании:

System.getProperty("user.home");

Ошибка JDK-6519127 была исправлена, и в разделе «Несовместимости между JDK 8 и JDK 7» примечаний к выпуску говорится:

Область: Core Libs / java.lang

конспект

Шаги, используемые для определения домашнего каталога пользователя в Windows, изменены в соответствии с рекомендуемым Microsoft подходом. Это изменение может наблюдаться в более старых выпусках Windows или в тех случаях, когда для параметров реестра или переменных среды заданы другие каталоги. Природа несовместимости

behavioral RFE

6519127

Несмотря на то, что вопрос старый, я оставляю это для дальнейшего использования.


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

Смотрите JavaDoc .


11
Нет, не правильный ответ, это тот же, что и выше. Да, я не только прочитал JavaDocs, но и попробовал его на всех платформах, прежде чем задавать этот вопрос! Ответ не так прост.
— Бруно Раншарт

3
Это может пойти ужасно неправильно на окнах, где он просто возьмет на себя родительский каталог каталога рабочего стола, который может быть где угодно ...
— Chronial

29

Концепция каталога HOME кажется немного расплывчатой, когда дело доходит до Windows. Если переменных среды (HOMEDRIVE / HOMEPATH / USERPROFILE) недостаточно, вам, возможно, придется прибегнуть к использованию встроенных функций через JNI или JNA . SHGetFolderPath позволяет вам извлекать специальные папки, такие как Мои документы (CSIDL_PERSONAL) или Локальные настройки \ Данные приложения (CSIDL_LOCAL_APPDATA).

Пример кода 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);

    }

}

К вашему сведению, папка, которая соответствует домашнему каталогу пользователя - CSIDL_PROFILE. См. Msdn.microsoft.com/en-us/library/bb762494(VS.85).aspx .
— Мэтт Solnit

Да, это сложная версия для случая Windows.
— Бруно Ranschaert

2
В последних версиях JNA (точнее jna-platform) есть класс Shell32Util, который очень красиво инкапсулирует соответствующий Windows API. В частности, целесообразно использовать Shell32Util.getKnownFolderPath (...) в сочетании с одной из констант из класса KnownFolders. Более старая функция API getFolderPath устарела начиная с Windows Vista.
— Себастьян Маршинг

17

Другие ответили на вопрос передо мной, но полезная программа для распечатки всех доступных свойств:

for (Map.Entry<?,?> e : System.getProperties().entrySet()) {
    System.out.println(String.format("%s = %s", e.getKey(), e.getValue())); 
}

Я бы не зависел от этого, потому что не все свойства стандартизированы. Вместо этого проверьте JavaDoc для System.getProperties (), чтобы узнать, какие свойства гарантированно существуют.
— Иоахим Зауэр

6
Это может быть правдой, но это все еще довольно полезно для новичка, я думаю! Я не уверен, что это заслуживает 2 downvotes :-(
— oxbow_lakes

6

Когда я искал версию Scala, все, что я мог найти, это код JD McDowell выше. Я включил свой порт Scala здесь, так как там в настоящее время нет более подходящего.

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

Как и в случае с версией Java, вам потребуется добавить собственный доступ к Java , включая оба jar-файла, в указанные библиотеки.

Приятно видеть, что JNA теперь делает это намного проще, чем когда был опубликован оригинальный код.


2

Я бы использовал алгоритм, подробно описанный в отчете об ошибках, используя System.getenv (String), и отступил бы до использования свойства user.dir, если ни одна из переменных среды не указала действительный существующий каталог. Это должно работать кроссплатформенно.

Я думаю, что под Windows, что вам действительно нужно, так это пользовательский каталог документов.


2

Альтернативой было бы использовать Apache CommonsIO FileUtils.getUserDirectory()вместо System.getProperty("user.home"). Это даст вам тот же результат, и нет никакой возможности ввести опечатку при указании системного свойства.

Существует большая вероятность, что в вашем проекте уже есть библиотека Apache CommonsIO. Не вводите его, если вы планируете использовать его только для получения домашнего каталога пользователя.


0

Если вы хотите что-то, что хорошо работает в Windows, есть пакет WinFoldersJava, который оборачивает собственный вызов для получения «специальных» каталогов в Windows. Мы часто используем его, и он работает хорошо.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.