Cách tốt nhất để tìm thư mục nhà của người dùng trong Java là gì?


279

Khó khăn là nó phải là nền tảng chéo. Windows 2000, XP, Vista, OSX, Linux, các biến thể unix khác. Tôi đang tìm kiếm một đoạn mã có thể thực hiện điều này cho tất cả các nền tảng và một cách để phát hiện nền tảng.

Bây giờ, bạn cần phải nhận thức lỗi 4787931user.homekhông làm việc một cách chính xác, vì vậy xin đừng cung cấp cho tôi về sách giáo khoa trả lời, tôi có thể tìm thấy những bản thân mình trong hướng dẫn sử dụng.


1
Bạn đã thử cách giải quyết được đề cập trong lỗi chưa? Có rất nhiều gợi ý.
Joachim Sauer

1
bug 4787931 cho các phiên bản java cho đến phiên bản 1.4.2 xuất hiện lại là bug 6519127 cho java 1.6. Vấn đề sẽ không biến mất và vẫn được liệt kê là ưu tiên thấp.
GregA100k

16
Lưu ý: lỗi 4787391 được đánh dấu là đã sửa trong Java 8
Steven R. Loomis

Câu trả lời:


364

Lỗi bạn tham chiếu (lỗi 4787391) đã được sửa trong Java 8. Ngay cả khi bạn đang sử dụng phiên bản Java cũ hơn, System.getProperty("user.home")cách tiếp cận có lẽ vẫn là tốt nhất. Cách user.hometiếp cận dường như làm việc trong một số lượng rất lớn các trường hợp. Một giải pháp chống đạn 100% trên Windows là khó, bởi vì Windows có một khái niệm thay đổi về ý nghĩa của thư mục chính.

Nếu user.homekhông đủ tốt cho bạn, tôi khuyên bạn nên chọn định nghĩa home directorycho windows và sử dụng nó, lấy biến môi trường phù hợp với System.getenv(String).


135

Thực tế với Java 8, cách đúng là sử dụng:

System.getProperty("user.home");

Lỗi JDK-6519127 đã được sửa và phần "Không tương thích giữa JDK 8 và JDK 7" của ghi chú phát hành nêu rõ:

Khu vực: Lõi Libs / java.lang

Tóm tắc

Các bước được sử dụng để xác định thư mục chính của người dùng trên Windows đã thay đổi để thực hiện theo phương pháp được đề xuất của Microsoft. Thay đổi này có thể được quan sát trên các phiên bản Windows cũ hơn hoặc nơi cài đặt đăng ký hoặc biến môi trường được đặt thành các thư mục khác. Bản chất của sự không tương thích

behavioral RFE

6519127

Mặc dù câu hỏi đã cũ nhưng tôi để lại điều này để tham khảo trong tương lai.


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

Xem JavaDoc .


11
Không, không phải là một câu trả lời đúng, đây là một câu như trên. Vâng, tôi không chỉ đọc JavaDocs, mà tôi cũng đã thử nó trên tất cả các nền tảng trước khi đặt câu hỏi này! Câu trả lời không đơn giản như vậy.
Bruno Ranschaert

3
Điều này có thể trở nên sai lầm khủng khiếp trên các cửa sổ, nơi nó sẽ chỉ lấy cha mẹ của thư mục máy tính để bàn, có thể là bất cứ nơi nào mà
Thời gian

29

Khái niệm về một thư mục HOME có vẻ hơi mơ hồ khi nói đến Windows. Nếu các biến môi trường (HOMEDRIVE / HOMEPATH / USERPROFILE) không đủ, bạn có thể phải sử dụng các hàm riêng thông qua JNI hoặc JNA . SHGetFolderPath cho phép bạn truy xuất các thư mục đặc biệt, như Tài liệu của tôi (CSIDL_PERSONAL) hoặc Cài đặt cục bộ \ Dữ liệu ứng dụng (CSIDL_LOCAL_APPDATA).

Mã JNA mẫu:

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

    }

}

FYI, thư mục tương ứng với thư mục chính của người dùng là CSIDL_PROFILE. Xem msdn.microsoft.com/en-us/l Library / bb762494 (VS85) .aspx .
Matt Solnit

Vâng, đây là một phiên bản phức tạp cho trường hợp Windows.
Bruno Ranschaert

2
Trong các phiên bản gần đây của JNA (chính xác hơn là nền tảng jna), có một lớp Shell32Util đóng gói API Windows tương ứng theo một cách rất hay. Cụ thể, sử dụng Shell32Util.getKnownFolderPath (...) kết hợp với một trong các hằng số từ lớp KnownFolders là phù hợp. Hàm API getFolderPath cũ hơn không được dùng kể từ Windows Vista.
Sebastian Marsched

17

Những người khác đã trả lời câu hỏi trước tôi nhưng một chương trình hữu ích để in ra tất cả các thuộc tính có sẵn là:

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

Tôi sẽ không phụ thuộc vào điều này, bởi vì không phải tất cả các thuộc tính đều được tiêu chuẩn hóa. Thay vào đó, hãy kiểm tra JavaDoc cho System.getProperIES () để tìm ra các thuộc tính nào được đảm bảo tồn tại.
Joachim Sauer

6
Điều đó có thể đúng nhưng nó vẫn khá hữu ích cho một người mới tôi nghĩ! Tôi không chắc chắn rằng nó xứng đáng với 2 lượt tải xuống :-(
oxbow_lakes

6

Khi tôi đang tìm kiếm phiên bản Scala, tất cả những gì tôi có thể tìm thấy là mã JNA của McDowell ở trên. Tôi bao gồm cổng Scala của tôi ở đây, vì hiện tại không có nơi nào phù hợp hơn.

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

Cũng như phiên bản Java, bạn sẽ cần thêm Quyền truy cập gốc Java , bao gồm cả các tệp jar, vào các thư viện được tham chiếu của bạn.

Thật tuyệt khi thấy rằng JNA bây giờ làm cho việc này dễ dàng hơn nhiều so với khi mã gốc được đăng.


2

Tôi sẽ sử dụng thuật toán chi tiết trong báo cáo lỗi bằng System.getenv (Chuỗi) và dự phòng sử dụng thuộc tính user.dir nếu không có biến môi trường nào chỉ ra thư mục hiện có hợp lệ. Điều này sẽ làm việc đa nền tảng.

Tôi nghĩ, trong Windows, thứ bạn thực sự theo đuổi là thư mục "tài liệu" nổi tiếng của người dùng.


2

Thay thế sẽ là sử dụng Apache CommonsIO FileUtils.getUserDirectory()thay vì System.getProperty("user.home"). Nó sẽ giúp bạn có được kết quả tương tự và không có cơ hội để giới thiệu một lỗi đánh máy khi chỉ định thuộc tính hệ thống.

Có một cơ hội lớn mà bạn đã có thư viện Apache CommonsIO trong dự án của mình. Đừng giới thiệu nó nếu bạn dự định chỉ sử dụng nó để lấy thư mục nhà của người dùng.


0

Nếu bạn muốn một cái gì đó hoạt động tốt trên windows, có một gói có tên WinFoldersJava bao bọc cuộc gọi riêng để nhận các thư mục 'đặc biệt' trên Windows. Chúng tôi sử dụng nó thường xuyên và nó hoạt động tốt.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.