2009-02-25 86 views
182

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 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.Cách tốt nhất để tìm thư mục nhà của người dùng trong Java là gì?

Bây giờ, bạn nên biết bug 4787931 rằng user.home không hoạt động chính xác, vì vậy vui lòng không cung cấp cho tôi câu trả lời sách giáo khoa, tôi có thể tự tìm thấy trong sách hướng dẫn.

+1

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

+1

lỗi 4787931 cho các phiên bản java lên đến 1.4.2 xuất hiện trở lại dưới dạng lỗi 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

+12

Lưu ý: lỗi 4787391 được đánh dấu là cố định trong Java 8 –

Trả lời

240

Lỗi bạn tham khảo (bug 4.787.391) đã được cố định trong Java 8. Thậm chí nếu bạn đang sử dụng phiên bản Java cũ hơn, cách tiếp cận System.getProperty("user.home") có lẽ vẫn là tốt nhất. Phương pháp user.home dường như hoạt động 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 rất khó, vì Windows có khái niệm chuyển dịch về thư mục chính.

Nếu user.home không đủ tốt cho bạn, tôi khuyên bạn nên chọn định nghĩa home directory cho cửa sổ và sử dụng nó, nhận biến môi trường thích hợp với System.getenv(String).

+2

Cuối cùng, đây là giải pháp tốt nhất sau khi tất cả –

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

Xem JavaDoc.

+8

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

+3

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

15

Những người khác đã trả lời câu hỏi trước khi 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())); 
} 
+0

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 tài sản được tiêu chuẩn hóa. Thay vào đó hãy kiểm tra JavaDoc cho System.getProperties() để tìm ra các thuộc tính nào được đảm bảo tồn tại. –

+5

Điều đó có thể đúng nhưng nó vẫn còn khá hữu ích cho một newbie tôi sẽ nghĩ! Tôi không chắc nó xứng đáng với 2 downvotes :-( –

26

Khái niệm về thư mục HOME có vẻ hơi mơ hồ khi nói đến Windows. Nếu environment variables (HOMEDRIVE/HOMEPATH/USERPROFILE) không đủ, bạn có thể phải sử dụng chức năng gốc thông qua JNI hoặc JNA. SHGetFolderPath cho phép bạn lấy lại các thư mục đặc biệt, như My Documents (CSIDL _ CÁ NHÂN) hoặc Local Settings \ Application Data (CSIDL _ ĐỊA PHƯƠNG _ APPDATA).

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

    } 

} 
+0

FYI, thư mục tương ứng với thư mục chính của người dùng là CSIDL_PROFILE. Xem http://msdn.microsoft.com/en -us/library/bb762494 (VS.85) .aspx –

+0

Có, đây là phiên bản phức tạp cho trường hợp Windows –

+0

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

2

Tôi sẽ sử dụng các thuật toán chi tiết trong báo cáo lỗi sử dụng System.getenv (String), và dự phòng để sử dụng tài sản user.dir nếu không ai trong số các biến môi trường chỉ định một 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, những gì bạn thực sự là thư mục "tài liệu" của người dùng.

4

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

Giống như phiên bản Java, bạn cần phải thêm Java Native Access, bao gồm cả tệp jar vào thư viện được tham chiếu của bạn.

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

87

Trên thực tế với Java 8 đúng cách là sử dụng:

System.getProperty("user.home"); 

Lỗi JDK-6519127 đã được cố định và "không tương thích giữa JDK 8 và JDK 7" của release notes trạng thái:

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

Bản tóm tắt

Thứ eps được sử dụng để xác định thư mục chính của người dùng trên Windows đã thay đổi theo cách tiếp cận được Microsoft khuyến nghị. Thay đổi này có thể được quan sát trên các phiên bản cũ hơn của Windows 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. Nature của không tương thích

behavioral RFE 

6519127 

Mặc dù câu hỏi là tuổi tôi rời này để tham khảo trong tương lai.

0

Nếu bạn muốn một cái gì đó hoạt động tốt trên các cửa sổ, có một gói gọi là WinFoldersJava kết thúc cuộc gọi bản địa để 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.

Các vấn đề liên quan