Apa cara terbaik untuk menemukan direktori home pengguna di Jawa?

280

Kesulitannya adalah itu harus lintas platform. Windows 2000, XP, Vista, OSX, Linux, varian unix lainnya. Saya mencari potongan kode yang dapat melakukan ini untuk semua platform, dan cara untuk mendeteksi platform.

Sekarang, Anda harus mengetahui bug 4787931 yang user.hometidak berfungsi dengan benar, jadi tolong jangan berikan saya jawaban buku teks, saya dapat menemukannya sendiri di manual.

Bruno Ranschaert
sumber
1
Apakah Anda mencoba solusi yang disebutkan dalam bug? Ada banyak saran.
Joachim Sauer
1
bug 4787931 untuk versi java hingga 1.4.2 muncul kembali sebagai bug 6519127 untuk java 1.6. Masalahnya tidak hilang dan masih terdaftar sebagai prioritas rendah.
GregA100k
16
Catatan: bug 4787391 ditandai sebagai diperbaiki di Java 8
Steven R. Loomis

Jawaban:

366

Bug yang Anda referensi (bug 4787391) telah diperbaiki di Java 8. Bahkan jika Anda menggunakan versi Java yang lebih lama, System.getProperty("user.home")pendekatannya mungkin masih yang terbaik. The user.homependekatan tampaknya bekerja dalam jumlah yang sangat besar kasus. Solusi antipeluru 100% pada Windows sulit, karena Windows memiliki konsep pergeseran tentang apa arti direktori home.

Jika user.hometidak cukup baik untuk Anda, saya sarankan memilih definisi home directoryuntuk windows dan menggunakannya, dapatkan variabel lingkungan yang sesuai System.getenv(String).

DJClayworth
sumber
136

Sebenarnya dengan Java 8 cara yang benar adalah menggunakan:

System.getProperty("user.home");

Bug JDK-6519127 telah diperbaiki dan bagian "Ketidakcocokan antara JDK 8 dan JDK 7" dari catatan rilis menyatakan:

Area: Core Libs / java.lang

Ringkasan

Langkah-langkah yang digunakan untuk menentukan direktori home pengguna di Windows telah berubah untuk mengikuti pendekatan yang direkomendasikan Microsoft. Perubahan ini dapat diamati pada edisi Windows yang lebih lama atau di mana pengaturan registri atau variabel lingkungan diatur ke direktori lain. Sifat Ketidakcocokan

behavioral RFE

6519127

Terlepas dari pertanyaan yang sudah tua saya meninggalkan ini untuk referensi di masa mendatang.

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

Lihat JavaDoc .

Joachim Sauer
sumber
11
Tidak, bukan jawaban yang benar, ini sama dengan di atas. Ya, saya tidak hanya membaca JavaDocs, tetapi saya juga mencobanya di semua platform sebelum mengajukan pertanyaan ini! Jawabannya tidak begitu sederhana.
Bruno Ranschaert
3
Ini bisa jadi sangat salah pada windows, di mana ia hanya akan mengambil induk dari direktori desktop, yang mungkin ada di mana saja ...
Chronial
29

Konsep direktori HOME tampaknya agak kabur ketika datang ke Windows. Jika variabel lingkungan (HOMEDRIVE / HOMEPATH / USERPROFILE) tidak cukup, Anda mungkin harus menggunakan fungsi asli melalui JNI atau JNA . SHGetFolderPath memungkinkan Anda untuk mengambil folder khusus, seperti My Documents (CSIDL_PERSONAL) atau Local Settings \ Data Aplikasi (CSIDL_LOCAL_APPDATA).

Contoh kode 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
sumber
FYI, folder yang sesuai dengan direktori home pengguna adalah CSIDL_PROFILE. Lihat msdn.microsoft.com/en-us/library/bb762494(VS.85).aspx .
Matt Solnit
Ya, ini adalah versi rumit untuk kasing Windows.
Bruno Ranschaert
2
Dalam versi JNA terbaru (lebih tepatnya jna-platform), ada kelas Shell32Util yang merangkum Windows API yang sesuai dengan cara yang sangat bagus. Secara khusus, menggunakan Shell32Util.getKnownFolderPath (...) dalam kombinasi dengan salah satu konstanta dari kelas FamousFolders harus sesuai. Fungsi getFolderPath API yang lebih lama tidak digunakan lagi sejak Windows Vista.
Sebastian Marsching
17

Orang lain telah menjawab pertanyaan di depan saya tetapi program yang berguna untuk mencetak semua properti yang tersedia adalah:

for (Map.Entry<?,?> e : System.getProperties().entrySet()) {
    System.out.println(String.format("%s = %s", e.getKey(), e.getValue())); 
}
oxbow_lakes
sumber
Saya tidak akan bergantung pada ini, karena tidak semua properti distandarisasi. Alih-alih memeriksa JavaDoc untuk System.getProperties () untuk mengetahui properti mana yang dijamin ada.
Joachim Sauer
6
Itu mungkin benar tetapi saya pikir itu masih cukup berguna bagi seorang pemula! Saya tidak yakin itu layak 2 downvotes :-(
oxbow_lakes
6

Ketika saya sedang mencari versi Scala, yang bisa saya temukan hanyalah kode JNA McDowell di atas. Saya menyertakan port Scala saya di sini, karena saat ini tidak ada tempat yang lebih tepat.

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

Seperti halnya versi Java, Anda harus menambahkan Java Native Access , termasuk kedua file jar, ke pustaka yang direferensikan.

Sangat menyenangkan untuk melihat bahwa JNA sekarang membuat ini jauh lebih mudah daripada ketika kode asli diposting.

Peter
sumber
2

Saya akan menggunakan algoritma yang dirinci dalam laporan bug menggunakan System.getenv (String), dan mundur untuk menggunakan properti user.dir jika tidak ada variabel lingkungan menunjukkan direktori yang ada yang valid. Ini harus bekerja lintas platform.

Saya pikir, di bawah Windows, apa yang benar-benar Anda cari adalah direktori "dokumen" nosional pengguna.

Lawrence Dol
sumber
2

Alternatifnya adalah menggunakan Apache CommonsIO FileUtils.getUserDirectory()alih-alihSystem.getProperty("user.home") . Ini akan memberi Anda hasil yang sama dan tidak ada kesempatan untuk memperkenalkan kesalahan ketik saat menentukan properti sistem.

Ada kemungkinan besar Anda sudah memiliki perpustakaan Apache CommonsIO di proyek Anda. Jangan perkenalkan jika Anda berencana menggunakannya hanya untuk mendapatkan direktori home user.

mladzo
sumber
0

Jika Anda menginginkan sesuatu yang berfungsi dengan baik di windows ada paket bernama WinFoldersJava yang membungkus panggilan asli untuk mendapatkan direktori 'khusus' pada Windows. Kami sering menggunakannya dan berfungsi dengan baik.

Neil Benn
sumber