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


279

困难在于它应该是跨平台的。Windows 2000,XP,Vista,OSX,Linux和其他Unix变体。我正在寻找可以针对所有平台完成此操作的代码片段,以及一种检测平台的方法。

现在,你应该知道的错误4787931user.home不能正常工作,所以请不要为我提供教科书的答案,我可以在手册中找到这些我自己。


1
您是否尝试了该错误中提到的解决方法?有很多建议。
约阿希姆·绍尔

1
直到1.4.2为止的Java版本的错误4787931再次显示为Java 1.6的错误6519127。问题不会消失,仍然被列为低优先级。
GregA100k,2009年

16
注意:错误4787391在Java 8
Steven R. Loomis

Answers:


364

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

如果user.home对您还不够好,我建议您home directory为Windows 选择一个定义并使用它,并使用获取适当的环境变量System.getenv(String)


135

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

System.getProperty("user.home");

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

区域:核心库/ java.lang

概要

用于确定Windows上用户主目录的步骤已更改为遵循Microsoft建议的方法。在Windows的较早版本上或将注册表设置或环境变量设置为其他目录的情况下,此更改可能可见。不兼容的性质

behavioral RFE

6519127

尽管问题很老,我还是留给以后参考。


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

参见JavaDoc


11
不,不是正确的答案,这与上述相同。是的,我不仅阅读了JavaDocs,而且还在提出这个问题之前在所有平台上进行了尝试!答案不是那么简单。
布鲁诺·兰沙特

3
在Windows上,这可能会导致严重错误,因为Windows只会使用桌面目录的父目录,而该目录可能在任何地方……
Chronial 2012年

29

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

    }

}

仅供参考,对应于用户主目录的文件夹是CSIDL_PROFILE。请参阅msdn.microsoft.com/zh-cn/library/bb762494(VS.85).aspx
马特·索尔尼特

是的,这是Windows机箱的精巧版本。
布鲁诺·兰斯哈特

2
在最新版本的JNA(更确切地说是jna平台)中,有一个Shell32Util类,它以一种非常不错的方式封装了相应的Windows API。特别是,将Shell32Util.getKnownFolderPath(...)与KnownFolders类的常量之一结合使用是合适的。从Windows Vista开始,不再使用较旧的getFolderPath API函数。
塞巴斯蒂安·马什兴

17

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

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

我不会依赖于此,因为并非所有属性都是标准化的。相反,请检查System.getProperties()的JavaDoc以找出保证存在的属性。
Joachim Sauer,2009年

6
可能是正确的,但是对于我认为的新手来说,它仍然非常有用!我不确定是否值得2次
投票

6

在搜索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现在比发布原始代码时更容易做到这一点。


2

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

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


2

替代方法是使用Apache CommonsIO FileUtils.getUserDirectory()代替System.getProperty("user.home")。指定系统属性时,将获得相同的结果,并且没有机会输入错字。

您的项目中很有可能已经有Apache CommonsIO库。如果您打算仅将其用于获取用户主目录,请不要对其进行介绍。


0

如果您想要在Windows上运行良好的工具,则有一个名为WinFoldersJava的软件包,该软件包包装了本地调用以获取Windows上的“特殊”目录。我们经常使用它,并且效果很好。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.