Quelle est la meilleure façon de trouver le répertoire personnel des utilisateurs en Java?

280

La difficulté est qu'elle doit être multiplateforme. Windows 2000, XP, Vista, OSX, Linux, autres variantes Unix. Je recherche un extrait de code qui peut accomplir cela pour toutes les plates-formes et un moyen de détecter la plate-forme.

Maintenant, vous devez être conscient du bogue 4787931 qui user.homene fonctionne pas correctement, alors ne me fournissez pas de réponses de manuel, je peux les trouver moi-même dans les manuels.

Bruno Ranschaert
la source
1
Avez-vous essayé les solutions de contournement mentionnées dans le bogue? Il y a plein de suggestions.
Joachim Sauer,
1
le bogue 4787931 pour les versions java jusqu'à 1.4.2 réapparaît comme bogue 6519127 pour java 1.6. Le problème ne disparaît pas et est toujours répertorié comme faible priorité.
GregA100k
16
Remarque: le bogue 4787391 est marqué comme corrigé dans Java 8
Steven R. Loomis

Réponses:

366

Le bogue auquel vous faites référence (bogue 4787391) a été corrigé dans Java 8. Même si vous utilisez une ancienne version de Java, l' System.getProperty("user.home")approche est probablement toujours la meilleure. L' user.homeapproche semble fonctionner dans un très grand nombre de cas. Une solution 100% pare-balles sur Windows est difficile, car Windows a un concept changeant de ce que signifie le répertoire personnel.

Si cela user.homene vous convient pas, je suggère de choisir une définition de home directorypour Windows et de l'utiliser, en obtenant la variable d'environnement appropriée avec System.getenv(String).

DJClayworth
la source
136

En fait, avec Java 8, la bonne façon est d'utiliser:

System.getProperty("user.home");

Le bogue JDK-6519127 a été corrigé et la section "Incompatibilités entre JDK 8 et JDK 7" des notes de version indique:

Zone: Core Libs / java.lang

Synopsis

Les étapes utilisées pour déterminer le répertoire de base de l'utilisateur sous Windows ont changé pour suivre l'approche recommandée par Microsoft. Cette modification peut être observable sur les anciennes éditions de Windows ou lorsque les paramètres de registre ou les variables d'environnement sont définis dans d'autres répertoires. Nature de l'incompatibilité

behavioral RFE

6519127

Bien que la question soit ancienne, je laisse cela pour référence future.

Paulo Fidalgo
la source
35
System.getProperty("user.home");

Voir JavaDoc .

Joachim Sauer
la source
11
Non, pas une bonne réponse, c'est la même que ci-dessus. Oui, je n'ai pas seulement lu les JavaDocs, mais je l'ai également essayé sur toutes les plateformes avant de poser cette question! La réponse n'est pas si simple.
Bruno Ranschaert
3
Cela pourrait aller horriblement mal sur Windows, où il faudra simplement prendre le parent du répertoire du bureau, qui pourrait être n'importe où…
Chronial
29

Le concept d'un répertoire HOME semble être un peu vague en ce qui concerne Windows. Si les variables d'environnement (HOMEDRIVE / HOMEPATH / USERPROFILE) ne suffisent pas, vous devrez peut-être recourir à des fonctions natives via JNI ou JNA . SHGetFolderPath vous permet de récupérer des dossiers spéciaux, comme Mes documents (CSIDL_PERSONAL) ou Paramètres locaux \ Données d'application (CSIDL_LOCAL_APPDATA).

Exemple de code 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);

    }

}
McDowell
la source
Pour info, le dossier qui correspond au répertoire personnel de l'utilisateur est CSIDL_PROFILE. Voir msdn.microsoft.com/en-us/library/bb762494(VS.85).aspx .
Matt Solnit
Oui, il s'agit d'une version élaborée pour le boîtier Windows.
Bruno Ranschaert
2
Dans les versions récentes de JNA (plus précisément jna-platform), il existe une classe Shell32Util qui encapsule très bien l'API Windows correspondante. En particulier, l'utilisation de Shell32Util.getKnownFolderPath (...) en combinaison avec l'une des constantes de la classe KnownFolders devrait être appropriée. L'ancienne fonction API getFolderPath est obsolète depuis Windows Vista.
Sebastian Marsching
17

D'autres ont répondu à la question avant moi, mais un programme utile pour imprimer toutes les propriétés disponibles est:

for (Map.Entry<?,?> e : System.getProperties().entrySet()) {
    System.out.println(String.format("%s = %s", e.getKey(), e.getValue())); 
}
oxbow_lakes
la source
Je ne dépendrais pas de cela, car toutes les propriétés ne sont pas normalisées. Vérifiez plutôt JavaDoc pour System.getProperties () pour savoir quelles propriétés sont garanties d'exister.
Joachim Sauer
6
C'est peut-être vrai, mais c'est quand même assez utile pour un débutant, je pense! Je ne suis pas sûr qu'il mérite 2 downvotes :-(
oxbow_lakes
6

Comme je cherchais la version Scala, tout ce que j'ai pu trouver était le code JNA de McDowell ci-dessus. J'inclus mon port Scala ici, car il n'y a actuellement aucun endroit plus approprié.

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

Comme pour la version Java, vous devrez ajouter Java Native Access , y compris les deux fichiers jar, à vos bibliothèques référencées.

C'est agréable de voir que la JNA rend maintenant cela beaucoup plus facile que lorsque le code d'origine a été publié.

Peter
la source
2

J'utiliserais l'algorithme détaillé dans le rapport de bogue en utilisant System.getenv (String), et je préférerais utiliser la propriété user.dir si aucune des variables d'environnement n'indiquait un répertoire existant valide. Cela devrait fonctionner sur plusieurs plates-formes.

Je pense que sous Windows, ce que vous cherchez vraiment, c'est le répertoire "documents" théorique de l'utilisateur.

Lawrence Dol
la source
2

Une alternative serait d'utiliser Apache CommonsIO FileUtils.getUserDirectory()au lieu de System.getProperty("user.home"). Cela vous donnera le même résultat et il n'y a aucune chance d'introduire une faute de frappe lors de la spécification de la propriété système.

Il y a de fortes chances que vous ayez déjà la bibliothèque Apache CommonsIO dans votre projet. Ne l'introduisez pas si vous prévoyez de l'utiliser uniquement pour obtenir le répertoire personnel de l'utilisateur.

mladzo
la source
0

Si vous voulez quelque chose qui fonctionne bien sur Windows, il existe un package appelé WinFoldersJava qui encapsule l'appel natif pour obtenir les répertoires «spéciaux» sur Windows. Nous l'utilisons fréquemment et cela fonctionne bien.

Neil Benn
la source