Який найкращий спосіб знайти домашній каталог користувачів на 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.