ทรัพยากร Java เป็นไฟล์


122

มีวิธีใน Java ในการสร้างอินสแตนซ์ไฟล์บนทรัพยากรที่ดึงมาจาก jar ผ่าน classloader หรือไม่

แอปพลิเคชันของฉันใช้ไฟล์บางไฟล์จาก jar (ค่าเริ่มต้น) หรือจากไดเร็กทอรีระบบไฟล์ที่ระบุในรันไทม์ (อินพุตของผู้ใช้) ฉันกำลังมองหาวิธีที่สอดคล้องกันในการ
a) การโหลดไฟล์เหล่านี้เป็นสตรีม
b) การแสดงรายการไฟล์ในไดเร็กทอรีที่ผู้ใช้กำหนดเองหรือไดเร็กทอรีใน jar ตามลำดับ

แก้ไข: เห็นได้ชัดว่าแนวทางที่ดีที่สุดคือการอยู่ห่างจาก java.io ไฟล์ทั้งหมด มีวิธีโหลดไดเร็กทอรีจาก classpath และแสดงรายการเนื้อหา (ไฟล์ / เอนทิตีที่อยู่ในนั้น) หรือไม่

คำตอบ:


58

ClassLoader.getResourceAsStreamและClass.getResourceAsStreamเป็นวิธีการโหลดข้อมูลทรัพยากรอย่างแน่นอน อย่างไรก็ตามฉันไม่เชื่อว่าจะมีวิธีใดในการ "แสดงรายการ" เนื้อหาขององค์ประกอบของ classpath

ในบางกรณีอาจเป็นไปไม่ได้ - ตัวอย่างเช่นClassLoader สามารถสร้างข้อมูลได้ทันทีโดยพิจารณาจากชื่อทรัพยากรที่ขอ หากคุณดูClassLoaderAPI (ซึ่งโดยพื้นฐานแล้วกลไกคลาสพา ธ ทำงานผ่าน) คุณจะเห็นว่าไม่มีอะไรให้ทำตามที่คุณต้องการ

หากคุณรู้ว่าคุณมีไฟล์ jar จริงๆคุณสามารถโหลดไฟล์นั้นZipInputStreamเพื่อดูว่ามีอะไรบ้าง หมายความว่าคุณจะมีรหัสที่แตกต่างกันสำหรับไดเรกทอรีและไฟล์ jar

อีกทางเลือกหนึ่งหากสร้างไฟล์แยกกันก่อนคือการรวมประเภทของไฟล์รายการที่มีรายการทรัพยากรที่มีอยู่ รวมสิ่งนั้นไว้ในไฟล์ jar หรือรวมไว้ในระบบไฟล์เป็นไฟล์และโหลดก่อนที่จะเสนอตัวเลือกทรัพยากรให้กับผู้ใช้


3
ฉันยอมรับว่ามันน่ารำคาญ - แต่มันทำให้ ClassLoader สามารถใช้ได้อย่างกว้างขวางมากขึ้นในรูปแบบอื่น ตัวอย่างเช่นการเขียน "web classloader" เป็นเรื่องง่ายเพราะเว็บเหมาะสำหรับการดึงไฟล์ แต่โดยทั่วไปแล้วจะไม่แสดงรายการไฟล์
Jon Skeet

ดูเหมือนว่าหากทรัพยากรที่คุณพยายามโหลดมีขนาดใหญ่กว่า 1MiB มากสิ่งที่InputStreamคุณได้รับจากการgetResourceAsStreamหยุดเพื่อดึงข้อมูลไบต์ของทรัพยากรหลังจากขนาดนั้นและส่งกลับค่า 0 iff ที่มีอยู่ในระบบไฟล์บีบอัดเช่น jar แทน ดูเหมือนคุณจะถูกบังคับให้ใช้getResourceและโหลดไฟล์โดยไม่ขึ้นอยู่กับสิ่งนี้ในกรณีนั้น
mgttlinger

1
@mgttlinger: ฟังดูไม่น่าจะเป็นไปได้สำหรับฉัน ... อาจจะคุ้มค่าที่จะโพสต์คำถามใหม่พร้อมคำติชมถ้าคุณทำได้
Jon Skeet

ปัญหาเกิดจากreadวิธีการตัดสินใจว่าจะอ่านข้อมูลมากน้อยเพียงใด (ฉันไม่ทราบ) และดูเหมือนว่าไฟล์ทั้งหมดจะถูกอ่านหากไฟล์ที่กำลังอ่านอยู่ในโฟลเดอร์ปกติ หากไฟล์อยู่ใน jar เนื่องจากถูกแพ็กเกจไฟล์จะอ่านเฉพาะบางส่วนเท่านั้น
mgttlinger

1
@mgttlinger: ไม่มันจะไม่อ่านเพียงบางส่วนของมัน - แต่อาจไม่เติมบัฟเฟอร์ทั้งหมดที่มีให้ในทุกครั้งที่อ่าน เช่นเคยInputStreamหากคุณต้องการอ่านทรัพยากรทั้งหมดคุณควรวนซ้ำจนกว่าจะreadส่งคืน -1
Jon Skeet

98

ฉันมีปัญหาเดียวกันและสามารถใช้สิ่งต่อไปนี้:

// Load the directory as a resource
URL dir_url = ClassLoader.getSystemResource(dir_path);
// Turn the resource into a File object
File dir = new File(dir_url.toURI());
// List the directory
String files = dir.list()

45
วิธีนี้ใช้ไม่ได้กับ JAR ใน classpath เฉพาะกับไฟล์และไดเร็กทอรี
yegor256

1
@ yegor256 หากไฟล์ของคุณอยู่ใน jar คุณสามารถสร้างFileSystemวัตถุและรับไฟล์Pathจากไฟล์. จากนั้นคุณสามารถอ่านได้ตามปกติ (ดูstackoverflow.com/a/22605905/1876344 )
mgttlinger

53

นี่คือรหัสเล็กน้อยจากหนึ่งในแอปพลิเคชันของฉัน ... โปรดแจ้งให้เราทราบหากเหมาะสมกับความต้องการของคุณ คุณสามารถใช้สิ่งนี้ได้หากคุณรู้จักไฟล์ที่คุณต้องการใช้

URL defaultImage = ClassA.class.getResource("/packageA/subPackage/image-name.png");
File imageFile = new File(defaultImage.toURI());

หวังว่าจะช่วยได้


ใช่จะทำ แต่toURI()จะล้มเหลว
Olivier Faucheux

10

วิธีที่เชื่อถือได้ในการสร้างอินสแตนซ์ไฟล์บนทรัพยากรที่ดึงมาจาก jar คือการคัดลอกทรัพยากรเป็นสตรีมไปยังไฟล์ชั่วคราว (ไฟล์ชั่วคราวจะถูกลบเมื่อ JVM ออก):

public static File getResourceAsFile(String resourcePath) {
    try {
        InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(resourcePath);
        if (in == null) {
            return null;
        }

        File tempFile = File.createTempFile(String.valueOf(in.hashCode()), ".tmp");
        tempFile.deleteOnExit();

        try (FileOutputStream out = new FileOutputStream(tempFile)) {
            //copy stream
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = in.read(buffer)) != -1) {
                out.write(buffer, 0, bytesRead);
            }
        }
        return tempFile;
    } catch (IOException e) {
        e.printStackTrace();
        return null;
    }
}

4

ลองสิ่งนี้:

ClassLoader.getResourceAsStream ("some/pkg/resource.properties");

มีวิธีการเพิ่มเติมเช่นดูที่นี่: http://www.javaworld.com/javaworld/javaqa/2003-08/01-qa-0808-property.html


ขอบคุณสำหรับคำตอบของคุณ ฉันรู้เกี่ยวกับ getResourceAsStream แต่ฉันคิดว่าฉันไม่สามารถโหลดไดเร็กทอรีจาก classpath ผ่านทางนั้นได้
Mantrum

ในไดเร็กทอรี Java เป็นไฟล์ (ราก UNIX ฉันเดา) ดังนั้นอย่างน้อยคุณควรลอง
topchef

ฉันคิดว่าจะแสดงรายการไฟล์ในไดเร็กทอรีคุณจะต้องใช้ java.io.File คุณสามารถค้นหาไฟล์ด้วย ClassLoader.findResource ซึ่งส่งคืน URL ที่สามารถส่งไปยังไฟล์
Nathan Voxland

2

นี่เป็นทางเลือกหนึ่ง: http://www.uofr.net/~greg/java/get-resource-listing.html


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