getResourceAsStream ส่งคืนค่า null


183

ฉันกำลังโหลดไฟล์ข้อความจากภายในแพ็คเกจใน JAR ที่คอมไพล์แล้วของโปรเจ็กต์ Java ของฉัน โครงสร้างไดเรกทอรีที่เกี่ยวข้องมีดังนี้:

/src/initialization/Lifepaths.txt

รหัสโหลดไฟล์ของฉันโดยการเรียกกลับClass::getResourceAsStreamInputStream

public class Lifepaths {
    public static void execute() {
        System.out.println(Lifepaths.class.getClass().
            getResourceAsStream("/initialization/Lifepaths.txt"));
    }

    private Lifepaths() {}

    //This is temporary; will eventually be called from outside
    public static void main(String[] args) {execute();}
}

การพิมพ์ออกมาจะพิมพ์เสมอnullไม่ว่าฉันจะใช้อะไร ฉันไม่แน่ใจว่าทำไมข้างต้นใช้งานไม่ได้ดังนั้นฉันจึงได้ลอง:

  • "/src/initialization/Lifepaths.txt"
  • "initialization/Lifepaths.txt"
  • "Lifepaths.txt"

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

ฉันจะแก้ปัญหานี้ได้อย่างไร


3
คุณได้ตรวจสอบว่ามันอยู่ในไฟล์ jar หรือไม่ คุณตรวจสอบกรอบไฟล์แล้วหรือยัง?
Jon Skeet

@JonSkeet แท้จริงแล้วจะถูกรวบรวมเป็นไฟล์ JAR ในตำแหน่งที่เหมาะสมและตัวพิมพ์เล็กนั้นถูกต้อง

1
@greedybuddha Lifepaths.classในขณะที่ฉันไม่สามารถเรียกว่าจากบริบทแบบคงที่ฉันสามารถเรียกใช้มัน ที่ถูกกล่าวว่าทำไมgetClassLoader()อนุญาตให้ทำงาน (นอกจากนี้อย่าลังเลที่จะโพสต์คำตอบ!)

คุณแสดงได้Lifepaths.getClass()ไหม ไม่มีวิธีการคงดังกล่าวกำหนดไว้ในวัตถุคือ ...
Puce

1
ดูคำตอบนี้และดูว่าคุณสามารถใช้งานgetResource(String)ได้หรือไม่ BTW - ฉันมักจะมีปัญหาในการทำให้ทั้งสองอย่างนี้ทำงานในstaticบริบท ปัญหาคือโดยพื้นฐานแล้วตัวโหลดคลาสที่ได้รับคือสิ่งที่มีไว้สำหรับคลาส J2SE คุณต้องได้รับการเข้าถึงคลาสโหลดเดอร์บริบทซึ่งมีไว้สำหรับแอปพลิเคชัน
Andrew Thompson

คำตอบ:


159

Lifepaths.class.getClass().getResourceAsStream(...) โหลดทรัพยากรโดยใช้ตัวโหลดคลาสระบบมันจะล้มเหลวอย่างเห็นได้ชัดเพราะไม่เห็น JAR ของคุณ

Lifepaths.class.getResourceAsStream(...) โหลดทรัพยากรโดยใช้ตัวโหลดคลาสเดียวกันที่โหลดคลาส Lifepaths และควรมีการเข้าถึงทรัพยากรใน JAR ของคุณ


129
เพียงเพื่อเพิ่มเมื่อเรียกใช้ getResourceAsStream (ชื่อ) ชื่อจะต้องเริ่มต้นด้วย "/" ฉันไม่แน่ใจว่าจำเป็นหรือไม่ แต่ฉันมีปัญหาหากไม่มี
David

3
ฉันกำลังทำเรื่องนี้ตั้งแต่ 8 โมงเช้านี้ / เมื่อวานนี้ ช่วยฉัน ฉันยังต้องการสแลชชั้นนำเพื่อให้ทำงานได้
kyle

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

3
@ David - ฉันคิดว่ามัน (นำหน้า '/') เป็นสิ่งที่จำเป็นมิฉะนั้นมันจะค้นหาสัมพันธ์กับแพ็คเกจ Lifepaths.class
Mz A

5
เพียงเพิ่มข้อมูลคุณจะต้องเพิ่ม/ก่อนที่เส้นทางของคุณหากไฟล์ของคุณอยู่ในไดเรกทอรีอื่น initialization/Lifepaths.txtเช่น หากเส้นทางของแฟ้มเป็นเดียวกันของชั้น Yout ( แต่ภายใต้ทรัพยากรที่เป็นผบหลัก) /คุณก็สามารถใส่ชื่อของไฟล์โดยไม่ต้องมี ตัวอย่างเช่นถ้าคลาสของคุณมีเส้นทางต่อไปนี้src/main/java/paths/Lifepaths.javaไฟล์ของคุณจะต้องมีเส้นทางsrc/main/resources/paths/Lifepaths.txtนี้
Dwhitz

59

กฎมีดังนี้:

  1. ตรวจสอบตำแหน่งของไฟล์ที่คุณต้องการโหลดภายใน JAR (และตรวจสอบให้แน่ใจว่าได้เพิ่มเข้าไปใน JAR ด้วย)
  2. ใช้ทั้งเส้นทางที่แน่นอน: เส้นทางเริ่มต้นที่รากของ JAR
  3. ใช้เส้นทางสัมพัทธ์: เส้นทางเริ่มต้นที่ไดเรกทอรีแพ็คเกจของคลาสที่คุณเรียกใช้ getResource / getResoucreAsStream

และลอง:

Lifepaths.class.getResourceAsStream("/initialization/Lifepaths.txt")

แทน

Lifepaths.class.getClass().getResourceAsStream("/initialization/Lifepaths.txt")

(ไม่แน่ใจว่ามันสร้างความแตกต่างหรือไม่ แต่ก่อนหน้านี้จะใช้ ClassLoader / JAR ที่ถูกต้องในขณะที่ฉันไม่แน่ใจกับตัวหลัง)


2
ฉันทำสิ่งทั้งสามนี้ไปแล้ว โปรดอ่านคำถามของฉันอีกครั้ง

จากคำถามของคุณยังไม่ชัดเจนว่า "โครงสร้างไดเรกทอรีที่เกี่ยวข้อง" คืออะไรและถ้าคุณได้ตรวจสอบจริงแล้วว่าไฟล์อยู่ใน JAR หรือไม่ (ขั้นตอนที่ 1)
Puce

1
คำพูดของคุณเกี่ยวกับเส้นทางญาติในที่สุดก็แก้ไขปัญหาในตอนท้ายของฉัน; ขอบคุณ!
พอลโบร์แมน

น่าสนใจ ปรากฎว่าฉันต้องทำ "/config.properties" (ด้วยเครื่องหมายทับ) เพื่อไปที่ ...
เอิร์ก

45

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

คำอธิบายที่ดีที่สุดที่ฉันเคยเห็นคือบทความนี้ JavaWorld ฉันจะสรุปที่นี่ แต่ถ้าคุณต้องการทราบข้อมูลเพิ่มเติมคุณควรตรวจสอบบทความ

วิธีการ

1) ClassLoader.getResourceAsStream()1)

รูปแบบ: "/" - ชื่อที่คั่น ไม่มีส่วนนำ "/" (ชื่อทั้งหมดเป็นแบบสัมบูรณ์)

ตัวอย่าง: this.getClass().getClassLoader().getResourceAsStream("some/pkg/resource.properties");

2) Class.getResourceAsStream()

รูปแบบ: "/" - ชื่อที่คั่น นำหน้า "/" หมายถึงชื่อแบบสัมบูรณ์ ชื่ออื่น ๆ ทั้งหมดจะสัมพันธ์กับแพ็คเกจของคลาส

ตัวอย่าง: this.getClass().getResourceAsStream("/some/pkg/resource.properties");


ตัวอย่างที่สองของคุณเสีย
Janus Troelsen

1
ตัวอย่างที่สองไม่ได้เสียหายอย่างที่ฉันพูดในคำตอบมันขึ้นอยู่กับที่ตั้งของทรัพยากร
greedybuddha

นอกจากนี้: ตรวจสอบให้แน่ใจว่า IDE ของคุณเห็นไฟล์ ('some / pkg / resource.properties') โดยการรีเฟรชโฟลเดอร์ซอร์ส
Eric Duminil

15

อย่าใช้พา ธ สัมบูรณ์ทำให้พวกมันสัมพันธ์กับไดเรกทอรี 'แหล่งข้อมูล' ในโครงการของคุณ รหัสที่รวดเร็วและสกปรกที่แสดงเนื้อหาของ MyTest.txt จาก 'ทรัพยากร' ของไดเรกทอรี

@Test
public void testDefaultResource() {
    // can we see default resources
    BufferedInputStream result = (BufferedInputStream) 
         Config.class.getClassLoader().getResourceAsStream("MyTest.txt");
    byte [] b = new byte[256];
    int val = 0;
    String txt = null;
    do {
        try {
            val = result.read(b);
            if (val > 0) {
                txt += new String(b, 0, val);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } 
    } while (val > -1);
    System.out.println(txt);
}

9

คุณอาจต้องการลองทำเช่นนี้เพื่อรับกระแสข้อมูลเช่นรับ URL ก่อนแล้วจึงเปิดเป็นสตรีม

URL url = getClass().getResource("/initialization/Lifepaths.txt"); 
InputStream strm = url.openStream(); 

ฉันเคยมีคำถามที่คล้ายกัน: การอ่านไฟล์ txt จาก jar ล้มเหลว แต่การอ่านภาพทำงานได้


3
สิ่งนี้เป็นสิ่งที่ getResourceAsStream () ทำ
fedeb

8

ดูเหมือนจะมีปัญหากับ ClassLoader ที่คุณใช้อยู่ ใช้ contextClassLoader เพื่อโหลดคลาส นี่คือโดยไม่คำนึงว่ามันเป็นวิธีคงที่ / ไม่คงที่

Thread.currentThread().getContextClassLoader().getResourceAsStream......


6

ฉันพบว่าตัวเองมีปัญหาที่คล้ายกัน เนื่องจากฉันใช้ maven ฉันต้องอัปเดต pom.xml ของฉันเพื่อรวมสิ่งนี้:

   ...
</dependencies>
<build>
    <resources>
        <resource>
            <directory>/src/main/resources</directory>
        </resource>
        <resource>
            <directory>../src/main/resources</directory>
        </resource>
    </resources>
    <pluginManagement>
        ...

หมายเหตุแท็กทรัพยากรในนั้นเพื่อระบุตำแหน่งของโฟลเดอร์นั้น หากคุณมีโครงการที่ซ้อนกัน (เช่นฉัน) คุณอาจต้องการรับทรัพยากรจากพื้นที่อื่นแทนที่จะอยู่ในโมดูลที่คุณกำลังทำงานซึ่งจะช่วยลดการเก็บไฟล์เดียวกันในแต่ละ repo หากคุณใช้ข้อมูลการตั้งค่าที่คล้ายกัน


3

สิ่งที่ใช้ได้ผลสำหรับฉันคือการเพิ่มไฟล์ภายใต้My Project/Java Resources/srcและจากนั้นใช้

this.getClass().getClassLoader().getResourceAsStream("myfile.txt");

ฉันไม่จำเป็นต้องเพิ่มไฟล์นี้ลงในพา ธ อย่างชัดเจน (เพิ่มใน/srcสิ่งที่เห็นได้ชัด)


ไวยากรณ์ java ผิด
Ilya Kharlamov

2

ไม่ทราบว่ามีความช่วยเหลือหรือไม่ แต่ในกรณีของฉันฉันมีทรัพยากรในโฟลเดอร์ / src / และได้รับข้อผิดพลาดนี้ ฉันย้ายรูปภาพไปยังโฟลเดอร์ bin และแก้ไขปัญหา


2

ตรวจสอบให้แน่ใจว่าไดเรกทอรีทรัพยากรของคุณ (เช่น "src") อยู่ใน classpath ของคุณ (ตรวจสอบให้แน่ใจว่ามันเป็นไดเรกทอรีแหล่งข้อมูลในเส้นทางการสร้างของคุณใน eclipse)

ตรวจสอบให้แน่ใจว่าได้โหลด clazz จาก classloader หลักแล้ว

จากนั้นเมื่อต้องการโหลด src / initialization / Lifepaths.txt ให้ใช้

clazz.getResourceAsStream("/initialization/Lifepaths.txt");

เหตุผล: clazz.getResourcesAsStream(foo)รูปลักษณ์ที่ขึ้นฟูจากภายใน classpath ของ clazz , เทียบกับชีวิตของไดเรกทอรี clazz ในใน "/" นำหน้าทำให้โหลดจากรูทของไดเรกทอรีใด ๆ ใน classpath ของ clazz

หากคุณไม่ได้อยู่ในคอนเทนเนอร์บางประเภทเช่น Tomcat หรือกำลังทำอะไรบางอย่างกับ ClassLoaders โดยตรงคุณก็สามารถปฏิบัติต่อคลาสพา ธ บรรทัดคำสั่ง eclipse / command เป็น classpather classloader เท่านั้น


2

JVM classloader ดีฟอลต์จะใช้ parent-classloader เพื่อโหลดรีซอร์สก่อน: deletegate แม่-ClassLoaderทรัพยากรโหลดครั้งแรก:

Lifepaths.class.getClass()'s classloader คือbootstrap classloaderดังนั้นgetResourceAsStreamจะค้นหา $ JAVA_HOME เท่านั้นโดยไม่คำนึงถึงผู้ใช้ที่ให้ไว้classpathไว้ เห็นได้ชัดว่า Lifepaths.txt ไม่ได้อยู่ที่นั่น

Lifepaths.class's classloader คือsystem classpath classloaderดังนั้นgetResourceAsStreamจะค้นหาผู้ใช้กำหนดclasspathและ Lifepaths.txt อยู่ที่นั่น

เมื่อใช้java.lang.Class#getResourceAsStream(String name)ชื่อที่ไม่ได้ขึ้นต้นด้วย'/'จะถูกเพิ่มด้วยpackage nameคำนำหน้า java.lang.ClassLoader#getResourceAsStreamหากคุณต้องการหลีกเลี่ยงปัญหานี้โปรดใช้ ตัวอย่างเช่น:

ClassLoader loader = Thread.currentThread().getContextClassLoader();
String resourceName = "Lifepaths.txt";
InputStream resourceStream = loader.getResourceAsStream(resourceName); 

2

พูดคร่าวๆ:

getClass().getResource("/") ~ = Thread.currentThread().getContextClassLoader().getResource(".")

สมมติว่าโครงสร้างโครงการของคุณเป็นดังนี้:

├── src
   ├── main
   └── test
   └── test
       ├── java
          └── com
              └── github
                  └── xyz
                      └── proj
                          ├── MainTest.java
                          └── TestBase.java
       └── resources
           └── abcd.txt
└── target
    └── test-classes
        ├── com
        └── abcd.txt
// in MainClass.java
this.getClass.getResource("/") -> "~/proj_dir/target/test-classes/"
this.getClass.getResource(".") -> "~/proj_dir/target/test-classes/com/github/xyz/proj/"
Thread.currentThread().getContextClassLoader().getResources(".") -> "~/proj_dir/target/test-classes/"
Thread.currentThread().getContextClassLoader().getResources("/") ->  null

2

ถ้าคุณใช้ Maven ต้องแน่ใจว่าบรรจุภัณฑ์ของคุณคือ 'jar' ไม่ใช่ 'pom'

<packaging>jar</packaging>

0

สิ่งที่คุณต้องการจริงๆคือ classPath แบบเต็มสำหรับไฟล์ ดังนั้นแทนที่จะคาดเดาลองค้นหารูตแล้วย้ายไฟล์ไปยังตำแหน่งฐานที่ดีกว่าโครงสร้างไฟล์ <.war> หนึ่งอัน ...

URL test1 = getClass().getResource("/");
URL test2 = getClass().getClassLoader().getResource("/");            
URL test3 = getClass().getClassLoader().getResource("../");

logger.info(test1.getPath()); 
logger.info(test2.getPath());
logger.info(test3.getPath());

0

สิ่งที่ใช้ได้ผลสำหรับฉันคือฉันวางไฟล์ไว้ด้านล่าง

src/main/java/myfile.log

และ

InputStream is = getClass().getClassLoader().getResourceAsStream("myfile.log");
        
        if (is == null) {
            throw new FileNotFoundException("Log file not provided");
        }

เรียกว่าโฟลเดอร์ซอร์สsrc/main/JAVAไฟล์ที่ไม่ใช่โค้ดของคุณไม่ควรอยู่ที่นี่
leyren

-12

@Emracool ... ฉันขอแนะนำให้คุณเลือก ดูเหมือนว่าคุณกำลังพยายามโหลดไฟล์ * .txt ดีกว่าที่จะใช้FileInputStream()แทนแล้วนี้น่ารำคาญหรือgetClass().getClassLoader().getResourceAsStream() getClass().getResourceAsStream()อย่างน้อยรหัสของคุณจะทำงานอย่างถูกต้อง


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