Який найкращий спосіб знайти домашній каталог користувачів на Java?


279

Складність полягає в тому, що це має бути крос-платформа. 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
Стівен Р. Луміс

Відповіді:


364

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

Якщо user.homeдля вас недостатньо добре, я б запропонував вибрати визначення home directoryдля Windows та використовувати його, отримуючи відповідну змінну середовища System.getenv(String).


135

Насправді з 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
Це може бути жахливо неправильним для Windows, де він просто візьме батьківський каталог робочого столу, який може бути де завгодно ...
Хронічний

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

    }

}

FYI, папка, яка відповідає домашньому каталогу користувача, - це CSIDL_PROFILE. Див. Msdn.microsoft.com/en-us/library/bb762494(VS.85).aspx .
Мет Соліт

Так, це розроблена версія для випадку Windows.
Бруно Раншаерт

2
В останніх версіях JNA (точніше jna-платформа) є клас Shell32Util, який дуже приємно інкапсулює відповідний API Windows. Зокрема, використання Shell32Util.getK knownFolderPath (...) у поєднанні з однією з констант класу KknownFolders повинно бути відповідним. Старіша функція 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 голоси :-(
oxbow_lakes

6

Коли я шукав версію Scala, все, що я міг знайти, - код JNA МакДауелла вище. Я включаю сюди свій порт 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 Native Access , включаючи обидва 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.