335

在 Java 中查找用户主目录的最佳方法是什么?

难点在于解决方案应该是跨平台的;它应该适用于 Windows 2000、XP、Vista、OS X、Linux 和其他 Unix 变体。我正在寻找可以在所有平台上完成此操作的代码片段,以及一种检测平台的方法。

根据 Java 错误4787931,系统属性user.home在 Windows XP 上无法正常工作,因此使用此系统属性不是可接受的解决方案,因为它不是跨平台的。

4

9 回答 9

429

您引用的错误(错误 4787391)已在 Java 8 中修复。即使您使用的是较旧版本的 Java,该System.getProperty("user.home")方法可能仍然是最好的。这种user.home方法似乎在很多情况下都有效。Windows 上的 100% 防弹解决方案很难,因为 Windows 对主目录的含义有一个不断变化的概念。

如果user.home对你来说还不够好,我建议选择home directoryfor windows 的定义并使用它,使用System.getenv(String).

于 2009-02-25T15:05:43.193 回答
161

实际上对于 Java 8,正确的方法是使用:

System.getProperty("user.home");

错误JDK-6519127已得到修复,发行说明的“JDK 8 和 JDK 7 之间的不兼容性”部分指出:

区域:核心库/java.lang

概要

用于在 Windows 上确定用户主目录的步骤已更改为遵循 Microsoft 推荐的方法。在旧版本的 Windows 或注册表设置或环境变量设置为其他目录的情况下,可能会观察到此更改。不兼容的性质

behavioral RFE

6519127

尽管问题很老,但我将其留作将来参考。

于 2014-11-13T14:52:00.223 回答
39
System.getProperty("user.home");

请参阅JavaDoc

于 2009-02-25T10:53:15.903 回答
30

对于 Windows,HOME 目录的概念似乎有点模糊。如果环境变量(HOMEDRIVE/HOMEPATH/USERPROFILE) 不够,您可能不得不通过JNIJNA使用本机函数。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);

    }

}
于 2009-02-25T17:10:09.663 回答
17

其他人已经回答了我面前的问题,但是打印出所有可用属性的有用程序是:

for (Map.Entry<?,?> e : System.getProperties().entrySet()) {
    System.out.println(String.format("%s = %s", e.getKey(), e.getValue())); 
}
于 2009-02-25T11:03:01.060 回答
9

替代方法是使用 Apache CommonsIOFileUtils.getUserDirectory()而不是System.getProperty("user.home"). 它将为您提供相同的结果,并且在指定系统属性时没有机会引入拼写错误。

There is a big chance you already have Apache CommonsIO library in your project. Don't introduce it if you plan to use it only for getting user home directory.

于 2018-10-11T10:36:15.903 回答
7

当我在搜索 Scala 版本时,我只能找到上面 McDowell 的 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 现在比发布原始代码时更容易做到这一点。

于 2014-06-22T13:30:32.797 回答
2

我将使用错误报告中详述的算法,使用 System.getenv(String),如果没有环境变量指示有效的现有目录,则回退到使用 user.dir 属性。这应该可以跨平台工作。

我认为,在 Windows 下,您真正​​追求的是用户的名义“文档”目录。

于 2009-02-26T08:34:43.857 回答
0

如果你想要一些在 Windows 上运行良好的东西,有一个名为WinFoldersJava的包,它包装了本机调用以获取 Windows 上的“特殊”目录。我们经常使用它并且效果很好。

于 2017-09-05T10:24:33.723 回答