วิธีโหลดไฟล์เป็น InputStream หลายวิธี


216

ความแตกต่างระหว่าง:

InputStream is = this.getClass().getClassLoader().getResourceAsStream(fileName)

และ

InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName)

และ

InputStream is = this.getClass().getResourceAsStream(fileName)

แต่ละคนมีความเหมาะสมที่จะใช้มากกว่าคนอื่นเมื่อใด

ไฟล์ที่ฉันต้องการอ่านอยู่ใน classpath เป็นคลาสของฉันที่อ่านไฟล์ คลาสและไฟล์ของฉันอยู่ใน jar เดียวกันและจัดเก็บในไฟล์ EAR และปรับใช้ใน WebSphere 6.1

คำตอบ:


289

มีความแตกต่างเล็กน้อยเกี่ยวกับวิธีการfileNameตีความของคุณผ่าน โดยทั่วไปคุณมี 2 วิธีที่แตกต่างกันและClassLoader.getResourceAsStream() Class.getResourceAsStream()วิธีการทั้งสองนี้จะค้นหาทรัพยากรที่แตกต่างกัน

ในClass.getResourceAsStream(path)เส้นทางถูกตีความว่าเป็นเส้นทางท้องถิ่นไปยังแพคเกจของชั้นเรียนที่คุณกำลังเรียกมันจาก ยกตัวอย่างเช่นโทรString.getResourceAsStream("myfile.txt")จะมองหาไฟล์ใน classpath "java/lang/myfile.txt"ของคุณในสถานที่ต่อไปนี้: หากเส้นทางของคุณเริ่มต้นด้วย a /ก็จะถือว่าเป็นเส้นทางที่แน่นอนและจะเริ่มค้นหาจากรูทของ classpath ดังนั้นการเรียกจะดูที่ตำแหน่งต่อไปในเส้นทางการเรียนของคุณString.getResourceAsStream("/myfile.txt")./myfile.txt

ClassLoader.getResourceAsStream(path)จะพิจารณาเส้นทางทั้งหมดให้เป็นเส้นทางที่แน่นอน ดังนั้นการโทรString.getClassLoader().getResourceAsStream("myfile.txt")และString.getClassLoader().getResourceAsStream("/myfile.txt")ทั้งสองจะค้นหาไฟล์ใน classpath ของคุณที่ตำแหน่งต่อไปนี้: ./myfile.txt.

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

ในกรณีของคุณคุณจะโหลดจากระดับ Application Server ดังนั้นคุณควรใช้แทนThread.currentThread().getContextClassLoader().getResourceAsStream(fileName) ยังจะทำงานthis.getClass().getClassLoader().getResourceAsStream(fileName)this.getClass().getResourceAsStream()

อ่านบทความนี้สำหรับข้อมูลโดยละเอียดเพิ่มเติมเกี่ยวกับปัญหานั้น ๆ


คำเตือนสำหรับผู้ใช้ Tomcat 7 และด้านล่าง

หนึ่งในคำตอบของคำถามนี้ระบุว่าคำอธิบายของฉันดูเหมือนจะไม่ถูกต้องสำหรับ Tomcat 7 ฉันพยายามตรวจดูว่าทำไมมันถึงเป็นเช่นนั้น

ดังนั้นฉันได้ดูซอร์สโค้ดของ Tomcat WebAppClassLoaderสำหรับTomcat หลายเวอร์ชัน การนำไปปฏิบัติของfindResource(String name)(ซึ่งรับผิดชอบการผลิต URL ไปยังทรัพยากรที่ร้องขอ) จะเหมือนกันใน Tomcat 6 และ Tomcat 7 แต่แตกต่างกันใน Tomcat 8

ในเวอร์ชัน 6 และ 7 การใช้งานไม่ได้พยายามทำให้ชื่อทรัพยากรเป็นมาตรฐาน ซึ่งหมายความว่าในเวอร์ชันเหล่านี้classLoader.getResourceAsStream("/resource.txt")อาจไม่สร้างผลลัพธ์เดียวกันกับclassLoader.getResourceAsStream("resource.txt")เหตุการณ์แม้ว่าควร (เนื่องจากสิ่งที่ Javadoc ระบุ) [รหัสแหล่งที่มา]

ในเวอร์ชัน 8 แม้ว่าชื่อทรัพยากรจะได้รับการทำให้เป็นมาตรฐานเพื่อรับรองว่าชื่อของทรัพยากรนั้นเป็นรุ่นที่ใช้จริง ดังนั้นใน Tomcat 8 การเรียกสองสายที่อธิบายไว้ข้างต้นควรส่งคืนผลลัพธ์เดียวกันเสมอ [รหัสแหล่งที่มา]

เป็นผลให้คุณต้องระมัดระวังเป็นพิเศษเมื่อใช้ClassLoader.getResourceAsStream()หรือClass.getResourceAsStream()บน Tomcat เวอร์ชันก่อนหน้านี้ 8 และคุณต้องจำไว้ว่าclass.getResourceAsStream("/resource.txt")สายจริงclassLoader.getResourceAsStream("resource.txt")( สายนำ/ถูกปล้น)


2
ผมค่อนข้างมั่นใจว่าจะทำงานที่แตกต่างจากgetClass().getResourceAsStream("/myfile.txt") getClassLoader().getResourceAsStream("/myfile.txt")
Brian Gordon

@BrianGordon: พวกเขาไม่ทำตัวแตกต่าง ที่จริงแล้ว javadoc สำหรับClass.getResourceAsStream (String)กล่าวว่าสิ่งต่อไปนี้: "วิธีนี้มอบหมายให้ผู้โหลดคลาสของวัตถุนี้" แล้วให้กฎจำนวนมากเกี่ยวกับวิธีการแปลงเส้นทางสัมพัทธ์เป็นเส้นทางที่แน่นอนก่อนที่จะมอบหมายให้ ClassLoader
LordOfThePigs

@LordOfThePigs ดูที่แหล่งข้อมูลจริง Class.getResourceAsStream ดึงเครื่องหมายทับไปข้างหน้าถ้าคุณระบุเส้นทางที่แน่นอน
Brian Gordon

4
@BrianGordon: ซึ่งทำให้มันทำงานเหมือนกับ ClassLoader.getResourceAsStream () ตั้งแต่หลังแปลเส้นทางทั้งหมดเป็นแบบสัมบูรณ์ไม่ว่าจะเริ่มต้นด้วยเครื่องหมายทับหรือไม่ ดังนั้นตราบใดที่เส้นทางของคุณเป็นจริงทั้งสองวิธีจะทำงานเหมือนกัน หากเส้นทางของคุณเป็นแบบสัมพันธ์พฤติกรรมจะแตกต่างกัน
LordOfThePigs

ฉันไม่สามารถหาgetClassLoader()ของStringมันเป็นความผิดพลาดหรือต้องการขยาย?
AAA

21

ใช้MyClass.class.getClassLoader().getResourceAsStream(path)เพื่อโหลดทรัพยากรที่เกี่ยวข้องกับรหัสของคุณ ใช้MyClass.class.getResourceAsStream(path)เป็นทางลัดและสำหรับทรัพยากรที่บรรจุในแพ็คเกจของชั้นเรียน

ใช้Thread.currentThread().getContextClassLoader().getResourceAsStream(path)เพื่อรับทรัพยากรที่เป็นส่วนหนึ่งของรหัสลูกค้าไม่ จำกัด ขอบเขตให้กับรหัสการโทร คุณควรระวังสิ่งนี้เนื่องจากตัวโหลดคลาสบริบทเธรดสามารถชี้ไปที่อะไรก็ได้


6

Java เก่าธรรมดาบน Java 7 ธรรมดาและไม่มีการอ้างอิงอื่นแสดงถึงความแตกต่าง ...

ฉันใส่file.txtในc:\temp\และฉันใส่c:\temp\ในคลาสพา ธ

มีเพียงกรณีเดียวที่มีความแตกต่างระหว่างการโทรสองครั้ง

class J {

 public static void main(String[] a) {
    // as "absolute"

    // ok   
    System.err.println(J.class.getResourceAsStream("/file.txt") != null); 

    // pop            
    System.err.println(J.class.getClassLoader().getResourceAsStream("/file.txt") != null); 

    // as relative

    // ok
    System.err.println(J.class.getResourceAsStream("./file.txt") != null); 

    // ok
    System.err.println(J.class.getClassLoader().getResourceAsStream("./file.txt") != null); 

    // no path

    // ok
    System.err.println(J.class.getResourceAsStream("file.txt") != null); 

   // ok
   System.err.println(J.class.getClassLoader().getResourceAsStream("file.txt") != null); 
  }
}

ขอบคุณมากสำหรับฉันทำงาน 'J.class.getResourceAsStream ("file.txt")'
abbasalim

3

คำตอบทั้งหมดเหล่านี้รอบที่นี่เช่นเดียวกับคำตอบในคำถามนี้แสดงให้เห็นว่าการโหลด URL ที่สมบูรณ์เช่น "/foo/bar.properties" ได้รับการรักษาเดียวกันและclass.getResourceAsStream(String) class.getClassLoader().getResourceAsStream(String)นี่ไม่ใช่กรณีอย่างน้อยไม่ได้อยู่ในการกำหนดค่า / รุ่น Tomcat ของฉัน (ปัจจุบัน 7.0.40)

MyClass.class.getResourceAsStream("/foo/bar.properties"); // works!  
MyClass.class.getClassLoader().getResourceAsStream("/foo/bar.properties"); // does NOT work!

ขออภัยฉันไม่มีคำอธิบายที่น่าพอใจอย่างแน่นอน แต่ฉันคิดว่าทอมแคทใช้กลอุบายที่สกปรกและเวทมนตร์สีดำของเขากับนักจัดคลาสและทำให้เกิดความแตกต่าง ฉันเคยใช้class.getResourceAsStream(String)ในอดีตและไม่เคยมีปัญหาใด ๆ

PS: ฉันโพสต์สิ่งนี้ที่นี่


บางทีแมวตัวผู้ตัดสินใจที่จะไม่เคารพข้อกำหนดและและไม่ปฏิบัติต่อทุกเส้นทางที่ผ่านไปClassLoader.getResourceAsStream()อย่างแน่นอน? นี่เป็นไปได้เนื่องจากมีการกล่าวถึงในความคิดเห็นบางส่วนข้างต้นClass.getResourceAsStreamจริง ๆ แล้วเรียกใช้ getClassLoader (). getResourceAsStream`
LordOfThePigs

หลังจากตรวจสอบในซอร์สโค้ดของ Java SE ฉันคิดว่าฉันถือคำตอบ: ทั้งคู่Class.getResourceAsStream()และClassLoader.getResourceAsStream()ท้ายที่สุดเรียกClassLoader.findResource()ซึ่งเป็นวิธีการป้องกันที่มีการใช้งานเริ่มต้นที่ว่างเปล่า แต่ javadoc ระบุอย่างชัดเจนว่า "การใช้งานตัวโหลดคลาสควรแทนที่วิธีนี้ เพื่อค้นหาทรัพยากร " ฉันสงสัยว่าการใช้ Tomcat ของวิธีการเฉพาะนี้อาจมีข้อบกพร่อง
LordOfThePigs

ผมเคยเทียบยังการดำเนินการWebAppClassLoader.findResource(String name)ในTomcat 7กับที่ของTomcat 8และปรากฏว่ามีความแตกต่างที่สำคัญ Tomcat 8 ทำให้ชื่อทรัพยากรเป็นปกติอย่างชัดเจนโดยการเพิ่มส่วนนำ/หากไม่มีชื่อใด ๆ ซึ่งทำให้ชื่อทั้งหมดเป็นชื่อที่แน่นอน Tomcat 7 ไม่ได้ เห็นได้ชัดว่าเป็นข้อบกพร่องใน Tomcat 7
LordOfThePigs

ฉันเพิ่มย่อหน้าเกี่ยวกับเรื่องนั้นในคำตอบของฉัน
LordOfThePigs

0

หลังจากลองวิธีการโหลดไฟล์โดยไม่ประสบความสำเร็จฉันจำได้ว่าฉันสามารถใช้FileInputStreamซึ่งทำงานได้อย่างสมบูรณ์

InputStream is = new FileInputStream("file.txt");

นี่เป็นอีกวิธีหนึ่งในการอ่านไฟล์ลงในไฟล์InputStreamซึ่งจะอ่านไฟล์จากโฟลเดอร์ที่กำลังทำงานอยู่


มันไม่ใช่ไฟล์มันเป็นทรัพยากร คำตอบไม่ถูกต้อง
มาร์ควิสแห่ง Lorne

1
@EJP ฉันสิ้นสุดในคำตอบ SO นี้ค้นหาวิธีการโหลดไฟล์โดยไม่ทราบความแตกต่างระหว่างไฟล์และทรัพยากร ฉันจะไม่ลบคำตอบเพราะอาจช่วยคนอื่นได้
António Almeida

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