What is the best way to find the user's home directory in Java?
Asked Answered
C

9

363

What is the best way to find the user's home directory in Java?

The difficulty is that the solution should be cross-platform; it should work on Windows 2000, XP, Vista, OS X, Linux, and other Unix variants. I am looking for a snippet of code that can accomplish this for all platforms, and a way to detect the platform.

Per Java bug 4787931, system property user.home does not work correctly on Windows XP, so using this system property is not an acceptable solution as it is not cross-platform.

Cowley answered 25/2, 2009 at 10:46 Comment(4)
Did you try the workarounds mentioned in the bug? There are plenty of suggestions.Lepidote
bug 4787931 for java versions up through 1.4.2 shows up again as bug 6519127 for java 1.6. The problem is not going away and is still listed as low priority.Ruppert
Note: bug 4787391 is marked as fixed in Java 8Heisser
For a discussion on Windows see https://mcmap.net/q/93708/-how-to-find-quot-my-documents-quot-folder-in-java-duplicate/873282.Gherlein
C
462

The bug you reference (bug 4787391) has been fixed in Java 8. Even if you are using an older version of Java, the System.getProperty("user.home") approach is probably still the best. The user.home approach seems to work in a very large number of cases. A 100% bulletproof solution on Windows is hard, because Windows has a shifting concept of what the home directory means.

If user.home isn't good enough for you I would suggest choosing a definition of home directory for windows and using it, getting the appropriate environment variable with System.getenv(String).

Cosine answered 25/2, 2009 at 15:5 Comment(1)
Apache has an excellent wrapper for System.getProperty calls that I recommend using. The correct call under that would be SystemUtils.getUserHome().Alloplasm
M
174

Actually with Java 8 the right way is to use:

System.getProperty("user.home");

The bug JDK-6519127 has been fixed and the "Incompatibilities between JDK 8 and JDK 7" section of the release notes states:

Area: Core Libs / java.lang

Synopsis

The steps used to determine the user's home directory on Windows have changed to follow the Microsoft recommended approach. This change might be observable on older editions of Windows or where registry settings or environment variables are set to other directories. Nature of Incompatibility

behavioral RFE

6519127

Despite the question being old I leave this for future reference.

Martlet answered 13/11, 2014 at 14:52 Comment(0)
L
43
System.getProperty("user.home");

See the JavaDoc.

Lepidote answered 25/2, 2009 at 10:53 Comment(2)
Nope, not a correct answer, this is the same one as above. Yes, I did not only read the JavaDocs, but I also tried it out on all platforms before asking this question! The answer is not so simple.Cowley
This might go horribly wrong on windows, where it will just take the parent of the desktop directory, which might be anywhere …Psalms
Y
32

The concept of a HOME directory seems to be a bit vague when it comes to Windows. If the environment variables (HOMEDRIVE/HOMEPATH/USERPROFILE) aren't enough, you may have to resort to using native functions via JNI or JNA. SHGetFolderPath allows you to retrieve special folders, like My Documents (CSIDL_PERSONAL) or Local Settings\Application Data (CSIDL_LOCAL_APPDATA).

Sample JNA code:

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

    }

}
Yann answered 25/2, 2009 at 17:10 Comment(3)
FYI, the folder that corresponds to the user's home directory is CSIDL_PROFILE. See msdn.microsoft.com/en-us/library/bb762494(VS.85).aspx.Calix
Yes, this is an elaborate version for the Windows case.Cowley
In recent versions of JNA (more precisely jna-platform), there is a Shell32Util class which encapsulates the corresponding Windows API in a very nice way. In particular, using Shell32Util.getKnownFolderPath(...) in combination with one of the constants from the KnownFolders class should be appropriate. The older getFolderPath API function is deprecated since Windows Vista.Glucoside
O
18

Others have answered the question before me but a useful program to print out all available properties is:

for (Map.Entry<?,?> e : System.getProperties().entrySet()) {
    System.out.println(String.format("%s = %s", e.getKey(), e.getValue())); 
}
Octennial answered 25/2, 2009 at 11:3 Comment(2)
I wouldn't depend on this, because not all properties are standardized. Instead check the JavaDoc for System.getProperties() to find out which properties are guaranteed to exist.Lepidote
That may be true but it's still pretty useful for a newbie I would think! I'm not sure it deserves 2 downvotes :-(Octennial
P
15

Alternative would be to use Apache CommonsIO FileUtils.getUserDirectory() instead of System.getProperty("user.home"). It will get you the same result and there is no chance to introduce a typo when specifying system property.

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.

Pliske answered 11/10, 2018 at 10:36 Comment(1)
File FileUtils.getUserDirectory() | String FileUtils.getUserDirectoryPath() - commons.apache.org/proper/commons-io/apidocs/org/apache/commons/…Ulcer
O
7

As I was searching for Scala version, all I could find was McDowell's JNA code above. I include my Scala port here, as there currently isn't anywhere more appropriate.

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")
            })
        }
    }
}

As with the Java version, you will need to add Java Native Access, including both jar files, to your referenced libraries.

It's nice to see that JNA now makes this much easier than when the original code was posted.

Odellodella answered 22/6, 2014 at 13:30 Comment(0)
N
2

I would use the algorithm detailed in the bug report using System.getenv(String), and fallback to using the user.dir property if none of the environment variables indicated a valid existing directory. This should work cross-platform.

I think, under Windows, what you are really after is the user's notional "documents" directory.

Neva answered 26/2, 2009 at 8:34 Comment(0)
W
0

If you want something that works well on windows there is a package called WinFoldersJava which wraps the native call to get the 'special' directories on Windows. We use it frequently and it works well.

Worldwide answered 5/9, 2017 at 10:24 Comment(1)
Link in answer is dead - "404 | This is not the web page you are looking for"Ulcer

© 2022 - 2024 — McMap. All rights reserved.