วิธีที่ดีที่สุดในการค้นหาโฮมไดเร็กตอรี่ของผู้ใช้ใน Java คืออะไร?


280

ปัญหาคือมันควรจะข้ามแพลตฟอร์ม Windows 2000, XP, Vista, OSX, Linux, ตัวแปร unix อื่น ๆ ฉันกำลังมองหาโค้ดบางส่วนที่สามารถทำสิ่งนี้ได้กับทุกแพลตฟอร์มและวิธีการตรวจสอบแพลตฟอร์ม

ตอนนี้คุณควรตระหนักถึงข้อผิดพลาด 4787931ที่user.homeทำงานไม่ถูกต้องดังนั้นโปรดอย่าให้คำตอบกับตำราเรียนฉันสามารถค้นหาสิ่งเหล่านี้ได้ในคู่มือ


1
คุณลองวิธีแก้ปัญหาที่กล่าวถึงในข้อผิดพลาดหรือไม่? มีคำแนะนำมากมาย
Joachim Sauer

1
ข้อผิดพลาด 4787931 สำหรับรุ่นจาวาถึง 1.4.2 แสดงขึ้นอีกครั้งเป็นข้อผิดพลาด 6519127 สำหรับจาวา 1.6 ปัญหาไม่ได้หายไปและยังคงอยู่ในรายการเป็นลำดับความสำคัญต่ำ
GregA100k

16
หมายเหตุ: ข้อผิดพลาด 4787391 ถูกทำเครื่องหมายว่าแก้ไขแล้วใน Java 8
Steven R. Loomis

คำตอบ:


366

ข้อผิดพลาดที่คุณอ้างอิง (ข้อผิดพลาด 4787391) ได้รับการแก้ไขใน Java 8 แม้ว่าคุณจะใช้ Java เวอร์ชันเก่ากว่าSystem.getProperty("user.home")วิธีการนี้อาจยังคงดีที่สุด user.homeวิธีดูเหมือนว่าจะทำงานในจำนวนมากของกรณี โซลูชันที่กันกระสุนได้ 100% ใน Windows นั้นยากเพราะ Windows มีแนวคิดที่เปลี่ยนไปตามความหมายของโฮมไดเร็กตอรี่

หากuser.homeไม่เพียงพอที่ดีสำหรับคุณฉันขอแนะนำให้เลือกนิยามของhome directoryสำหรับ Windows System.getenv(String)และใช้มันได้รับตัวแปรสภาพแวดล้อมที่เหมาะสมกับ


136

จริงๆแล้วด้วย Java 8 วิธีที่ถูกต้องคือการใช้:

System.getProperty("user.home");

ข้อผิดพลาดJDK-6519127ได้รับการแก้ไขและส่วน "ความเข้ากันไม่ได้ระหว่าง JDK 8 และ JDK 7" ของสถานะบันทึกประจำรุ่น :

พื้นที่: Core Libs / java.lang

สรุป

ขั้นตอนที่ใช้ในการกำหนดโฮมไดเร็กตอรี่ของผู้ใช้ใน Windows ได้เปลี่ยนไปตามแนวทางที่ Microsoft แนะนำ การเปลี่ยนแปลงนี้อาจสังเกตได้ใน Windows รุ่นเก่าหรือที่การตั้งค่ารีจิสทรีหรือตัวแปรสภาพแวดล้อมถูกตั้งค่าเป็นไดเรกทอรีอื่น ลักษณะของความไม่ลงรอยกัน

behavioral RFE

6519127

แม้คำถามจะแก่ แต่ฉันก็ทิ้งเรื่องนี้ไว้เพื่อใช้อ้างอิงในอนาคต


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

ดูJavaDoc


11
ไม่ไม่ใช่คำตอบที่ถูกต้องนี่เป็นคำตอบเดียวกับข้างบน ใช่ฉันไม่เพียง แต่อ่าน JavaDocs เท่านั้น แต่ฉันได้ทดลองใช้กับทุกแพลตฟอร์มก่อนถามคำถามนี้! คำตอบนั้นไม่ง่ายนัก
Bruno Ranschaert

3
นี่อาจจะผิดอย่างผิดพลาดบน windows ซึ่งมันจะพาพาเรนต์ของไดเรกทอรีเดสก์ท็อปไปซึ่งอาจจะอยู่ที่ใดก็ได้ ...
Chronial

29

แนวคิดของไดเร็กทอรี HOME ดูเหมือนจะคลุมเครือเล็กน้อยเมื่อพูดถึง Windows ถ้าตัวแปรสภาพแวดล้อม (HOMEDRIVE / HOMEPATH / USERPROFILE) จะไม่พอคุณอาจต้องรีสอร์ทเพื่อใช้ฟังก์ชั่นพื้นเมืองผ่านJNIหรือJNA SHGetFolderPathช่วยให้คุณสามารถเรียกคืนโฟลเดอร์พิเศษเช่นMy Documents (CSIDL_PERSONAL) หรือLocal Settings \ Application Data (CSIDL_LOCAL_APPDATA)

ตัวอย่างรหัส 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);

    }

}

FYI โฟลเดอร์ที่สอดคล้องกับโฮมไดเรกทอรีของผู้ใช้คือ CSIDL_PROFILE ดูmsdn.microsoft.com/en-us/library/bb762494(VS.85).aspx
Matt Solnit

ใช่นี่เป็นเวอร์ชั่นที่ซับซ้อนสำหรับเคส Windows
Bruno Ranschaert

2
ในรุ่นล่าสุดของ JNA (JNA แพลตฟอร์มที่แม่นยำยิ่งขึ้น) มีคลาส Shell32Util ซึ่งห่อหุ้ม Windows API ที่สอดคล้องกันในวิธีที่ดีมาก โดยเฉพาะอย่างยิ่งการใช้ Shell32Util.getKnownFolderPath (... ) ร่วมกับหนึ่งในค่าคงที่จากคลาส KnownFolders ควรเหมาะสม ฟังก์ชัน getFolderPath API ที่เก่ากว่าเลิกใช้งานแล้วตั้งแต่ Windows Vista
เซบาสเตียน Marsching

17

คนอื่น ๆ ได้ตอบคำถามก่อนหน้าฉัน แต่โปรแกรมที่มีประโยชน์ในการพิมพ์คุณสมบัติที่มีทั้งหมดคือ

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

ฉันจะไม่พึ่งพาสิ่งนี้เพราะคุณสมบัติไม่ได้มาตรฐานทั้งหมด แทนที่จะตรวจสอบ JavaDoc สำหรับ System.getProperties () เพื่อค้นหาว่าคุณสมบัติใดรับประกันได้ว่ามีอยู่
โจอาคิมซาวเออร์

6
อาจเป็นเรื่องจริง แต่ก็ยังมีประโยชน์สำหรับมือใหม่ที่ฉันคิดว่า! ฉันไม่แน่ใจว่ามันสมควรได้รับ downvotes 2 :-(
oxbow_lakes

6

ขณะที่ฉันค้นหารุ่น Scala ทั้งหมดที่ฉันสามารถค้นหาได้คือรหัส JNA ของ McDowell ด้านบน ฉันรวมพอร์ต Scala ของฉันที่นี่เนื่องจากไม่มีความเหมาะสมมากกว่า

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

เช่นเดียวกับเวอร์ชัน Java คุณจะต้องเพิ่มJava Native Accessรวมถึงไฟล์ jar ทั้งสองลงในไลบรารีที่อ้างอิงของคุณ

ยินดีที่ได้เห็นว่าตอนนี้ JNA ทำให้ง่ายกว่าตอนที่โพสต์รหัสต้นฉบับ


2

ฉันจะใช้อัลกอริทึมที่มีรายละเอียดในรายงานข้อผิดพลาดโดยใช้ System.getenv (String) และทางเลือกในการใช้คุณสมบัติ user.dir หากไม่มีตัวแปรสภาพแวดล้อมที่ระบุว่าไดเรกทอรีที่มีอยู่ถูกต้อง สิ่งนี้จะทำงานข้ามแพลตฟอร์มได้

ฉันคิดว่าภายใต้ Windows สิ่งที่คุณเป็นจริงคือไดเรกทอรี "เอกสาร" ของผู้ใช้


2

ทางเลือกอื่นคือใช้ Apache CommonsIO FileUtils.getUserDirectory()แทนSystem.getProperty("user.home")แทนมันจะทำให้คุณได้ผลลัพธ์เดียวกันและไม่มีโอกาสที่จะแนะนำการพิมพ์ผิดเมื่อระบุคุณสมบัติของระบบ

มีโอกาสใหญ่ที่คุณมีห้องสมุด Apache CommonsIO ในโครงการของคุณแล้ว อย่าแนะนำถ้าคุณวางแผนที่จะใช้เพื่อรับไดเรกทอรีบ้านของผู้ใช้เท่านั้น


0

หากคุณต้องการบางสิ่งที่ทำงานได้ดีบน windows มีแพ็คเกจที่เรียกว่าWinFoldersJavaซึ่งล้อมสายดั้งเดิมเพื่อรับไดเรกทอรี 'พิเศษ' บน Windows เราใช้บ่อยและใช้งานได้ดี

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.