Каков наилучший способ найти домашний каталог пользователей на 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).

DJClayworth
источник
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())); 
}
oxbow_lakes
источник
Я бы не зависел от этого, потому что не все свойства стандартизированы. Вместо этого проверьте 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. Не вводите его, если вы планируете использовать его только для получения домашнего каталога пользователя.

mladzo
источник
0

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

Нил Бенн
источник