อ่านไฟล์ทรัพยากรจากภายในขวด


242

ฉันต้องการอ่านทรัพยากรจากในขวดของฉันเช่น:

File file;
file = new File(getClass().getResource("/file.txt").toURI());
BufferredReader reader = new BufferedReader(new FileReader(file));

//Read the file

และใช้งานได้ดีเมื่อรันใน Eclipse แต่ถ้าฉันส่งออกไปยัง jar การรันจะมี IllegalArgumentException:

Exception in thread "Thread-2"
java.lang.IllegalArgumentException: URI is not hierarchical

และฉันไม่รู้จริงๆว่าทำไม แต่ด้วยการทดสอบบางอย่างฉันพบว่าฉันเปลี่ยน

file = new File(getClass().getResource("/file.txt").toURI());

ถึง

file = new File(getClass().getResource("/folder/file.txt").toURI());

จากนั้นทำงานตรงกันข้าม (ใช้งานได้ใน jar แต่ไม่ใช่คราส)

ฉันใช้ Eclipse และโฟลเดอร์ที่มีไฟล์ของฉันอยู่ในโฟลเดอร์ class


หากคุณต้องการอ่านไฟล์จากไดเรกทอรีในขวดด้วยตัวเลขใด ๆ สำหรับไฟล์โปรดดูStackoverflow-Link
Michael Hegner

1
ฉันไม่แน่ใจว่าคำถามดั้งเดิมเกี่ยวข้องกับฤดูใบไม้ผลิหรือไม่ ลิงค์ในความคิดเห็นก่อนหน้าอ้างถึงคำตอบเฉพาะของ Spring จากคำถามอื่น ฉันเชื่อว่าgetResourceAsStreamยังคงเป็นวิธีการแก้ปัญหาที่ง่ายและพกพาได้มากกว่า
Drew MacInnis

คำตอบ:


398

แทนที่จะพยายามจัดการทรัพยากรเป็นไฟล์ให้ขอClassLoaderส่งคืนInputStreamสำหรับทรัพยากรแทนผ่านgetResourceAsStream :

InputStream in = getClass().getResourceAsStream("/file.txt"); 
BufferedReader reader = new BufferedReader(new InputStreamReader(in));

ตราบใดที่file.txtทรัพยากรที่มีอยู่ใน classpath แล้ววิธีการนี้จะทำงานในลักษณะเดียวกันโดยไม่คำนึงถึงว่าfile.txtทรัพยากรที่อยู่ในไดเรกทอรีหรือภายในclasses/jar

URI is not hierarchicalเกิดขึ้นเนื่องจาก URI file:/example.jar!/file.txtสำหรับทรัพยากรภายในไฟล์ขวดเป็นไปเพื่อสิ่งที่มีลักษณะเช่นนี้ คุณไม่สามารถอ่านรายการภายในjar(เป็นzipไฟล์) เหมือนมันเป็นธรรมดาเก่าไฟล์

นี่คือคำอธิบายที่ดีโดย:


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

1
โอ้และ BTW เหตุผลที่getClass().getResource("**/folder**/file.txt")ทำให้การทำงานเป็นเพราะฉันมีโฟลเดอร์นั้นในไดเรกทอรีเดียวกันกับขวดของฉัน :)
PrinceCJC

5
getResourceAsStreamส่งกลับค่า null ถ้าทรัพยากรไม่มีอยู่เพื่อให้สามารถทดสอบ "มีอยู่" ของคุณ
Drew MacInnis

1
BTW คุณมีการพิมพ์ผิด: ควรเป็น BufferedReader ไม่ใช่ BufferredReader (สังเกตว่า 'r' พิเศษในภายหลัง)
mailmindlin

2
และแน่นอน ... อย่าลืมปิด inputStream และ BufferedReader
Noremac

26

ในการเข้าถึงไฟล์ในโถคุณมีสองตัวเลือก:

  • วางไฟล์ในโครงสร้างไดเรกทอรีที่ตรงกับชื่อแพ็คเกจของคุณ (หลังจากแตกไฟล์. jar ควรอยู่ในไดเรกทอรีเดียวกับไฟล์. class) จากนั้นเข้าถึงโดยใช้ getClass().getResourceAsStream("file.txt")

  • วางไฟล์ที่รูท (หลังจากแตกไฟล์. jar ควรอยู่ในรูท) จากนั้นเข้าถึงโดยใช้ Thread.currentThread().getContextClassLoader().getResourceAsStream("file.txt")

ตัวเลือกแรกอาจไม่ทำงานเมื่อใช้ jar เป็นปลั๊กอิน


ขอบคุณมากสำหรับคำตอบที่ยอดเยี่ยมนี้ ... ประโยชน์อีกประการหนึ่งของตัวเลือกที่สองคือมันใช้งานได้จากวิธีการหลักเนื่องจาก getClass () ใช้งานได้จากอินสแตนซ์เท่านั้นและไม่ใช่จากวิธีการคงที่
Dan Ortega

6

ฉันมีปัญหานี้มาก่อนและฉันใช้วิธีสำรองเพื่อโหลด วิธีแรกโดยทั่วไปทำงานภายในไฟล์. jar และวิธีที่สองทำงานภายใน eclipse หรือ IDE อื่น ๆ

public class MyClass {

    public static InputStream accessFile() {
        String resource = "my-file-located-in-resources.txt";

        // this is the path within the jar file
        InputStream input = MyClass.class.getResourceAsStream("/resources/" + resource);
        if (input == null) {
            // this is how we load file within editor (eg eclipse)
            input = MyClass.class.getClassLoader().getResourceAsStream(resource);
        }

        return input;
    }
}

3

จนถึงตอนนี้ (ธันวาคม 2017) นี่เป็นทางออกเดียวที่ฉันพบว่าใช้ได้ทั้งภายในและภายนอก IDE

ใช้PathMatchingResourcePatternResolver

หมายเหตุ:มันยังทำงานใน spring-boot ด้วย

ในตัวอย่างนี้ฉันกำลังอ่านไฟล์บางไฟล์ที่อยู่ในsrc / main / resources / my_folder :

try {
    // Get all the files under this inner resource folder: my_folder
    String scannedPackage = "my_folder/*";
    PathMatchingResourcePatternResolver scanner = new PathMatchingResourcePatternResolver();
    Resource[] resources = scanner.getResources(scannedPackage);

    if (resources == null || resources.length == 0)
        log.warn("Warning: could not find any resources in this scanned package: " + scannedPackage);
    else {
        for (Resource resource : resources) {
            log.info(resource.getFilename());
            // Read the file content (I used BufferedReader, but there are other solutions for that):
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(resource.getInputStream()));
            String line = null;
            while ((line = bufferedReader.readLine()) != null) {
                // ...
                // ...                      
            }
            bufferedReader.close();
        }
    }
} catch (Exception e) {
    throw new Exception("Failed to read the resources folder: " + e.getMessage(), e);
}

3

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

ในกรณีนี้วิธีแก้ปัญหาหนึ่งคือการคัดลอกเนื้อหาทรัพยากรลงในไฟล์ชั่วคราว ตัวอย่างต่อไปนี้ใช้ TemporaryFolderjUnit

    private List<String> decomposePath(String path){
        List<String> reversed = Lists.newArrayList();
        File currFile = new File(path);
        while(currFile != null){
            reversed.add(currFile.getName());
            currFile = currFile.getParentFile();
        }
        return Lists.reverse(reversed);
    }

    private String writeResourceToFile(String resourceName) throws IOException {
        ClassLoader loader = getClass().getClassLoader();
        InputStream configStream = loader.getResourceAsStream(resourceName);
        List<String> pathComponents = decomposePath(resourceName);
        folder.newFolder(pathComponents.subList(0, pathComponents.size() - 1).toArray(new String[0]));
        File tmpFile = folder.newFile(resourceName);
        Files.copy(configStream, tmpFile.toPath(), REPLACE_EXISTING);
        return tmpFile.getAbsolutePath();
    }

นี่มันวาวไปเยอะ ขอบคุณที่บันทึกไว้ ฉันอยากรู้ว่ามีตัวเลือกอื่นใดบ้างหากไม่มี JUnit
Alex Moore-Niemi

0

หากคุณต้องการอ่านเป็นไฟล์ฉันเชื่อว่ายังมีวิธีแก้ปัญหาที่คล้ายกัน:

    ClassLoader classLoader = getClass().getClassLoader();
    File file = new File(classLoader.getResource("file/test.xml").getFile());

4
URL.getFile () ไม่แปลง URL เป็นชื่อไฟล์ จะส่งคืนส่วนของ URL หลังจากโฮสต์โดยมีการเข้ารหัสเปอร์เซ็นต์ครบถ้วนดังนั้นหากเส้นทางมีอักขระที่ไม่ใช่ ASCII หรืออักขระ ASCII ใด ๆ ที่ไม่ได้รับอนุญาตใน URL (รวมถึงช่องว่าง) ผลลัพธ์จะไม่เป็นชื่อไฟล์ที่มีอยู่ แม้ว่า URL นั้นจะเป็นfile:URL ก็ตาม
VGR

48
สิ่งนี้จะไม่ทำงานภายในเมื่อโปรแกรมสร้างลงในโถ
Akshay Kasar

1
ไม่ทำงานจาก jar เว้นแต่คุณจะแปลงเป็นสตริงและบันทึกไว้ในเครื่องก่อน
smoosh911

0

ตรวจสอบให้แน่ใจว่าคุณทำงานกับตัวคั่นที่ถูกต้อง ฉันแทนที่ทั้งหมดในเส้นทางสัมพันธ์กับ/ File.separatorสิ่งนี้ทำงานได้ดีใน IDE แต่ไม่ได้ทำงานใน build JAR


0

ฉันคิดว่านี่ควรจะทำงานใน java เช่นกัน รหัสต่อไปนี้ที่ฉันใช้คือใช้ kotlin

val resource = Thread.currentThread().contextClassLoader.getResource('resources.txt')

0

ฉันพบวิธีแก้ไขแล้ว

BufferedReader br = new BufferedReader(new InputStreamReader(Main.class.getResourceAsStream(path)));

แทนที่ "Main" ด้วยคลาส java ที่คุณเขียนโค้ดไว้แทนที่ "path" ด้วยพา ธ ภายในไฟล์ jar

ตัวอย่างเช่นถ้าคุณใส่ State1.txt ในแพ็คเกจ com.issac.state ให้พิมพ์พา ธ เป็น "/ com / issac / state / State / State1" ถ้าคุณใช้ Linux หรือ Mac หากคุณใช้ Windows ให้พิมพ์พา ธ เป็น "\ com \ issac \ state \ State1" อย่าเพิ่มนามสกุล. txt ลงในไฟล์ยกเว้นว่าจะเกิดข้อยกเว้น File not found


-1

คุณสามารถใช้คลาสโหลดเดอร์ซึ่งจะอ่านจาก classpath เป็นเส้นทางรูท (โดยไม่มี "/" ในตอนเริ่มต้น)

InputStream in = getClass().getClassLoader().getResourceAsStream("file.txt"); 
BufferedReader reader = new BufferedReader(new InputStreamReader(in));

-1

หากคุณกำลังใช้สปริงคุณสามารถใช้วิธีการต่อไปนี้เพื่ออ่านไฟล์จาก src / main / resources:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.springframework.core.io.ClassPathResource;

  public String readFileToString(String path) throws IOException {

    StringBuilder resultBuilder = new StringBuilder("");
    ClassPathResource resource = new ClassPathResource(path);

    try (
        InputStream inputStream = resource.getInputStream();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) {

      String line;

      while ((line = bufferedReader.readLine()) != null) {
        resultBuilder.append(line);
      }

    }

    return resultBuilder.toString();
  }

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

นี่จะเป็นการลบตัวแบ่งบรรทัดในไฟล์!
elad.chen

-1

ด้วยเหตุผลบางอย่างclassLoader.getResource()ส่งคืน null เสมอเมื่อฉันปรับใช้เว็บแอ็พพลิเคชันกับ WildFly 14. รับ classLoader จากgetClass().getClassLoader()หรือThread.currentThread().getContextClassLoader()ส่งคืน null

getClass().getClassLoader() API doc กล่าวว่า

"ส่งคืนคลาสโหลดเดอร์สำหรับคลาสการใช้งานบางอย่างอาจใช้ null เพื่อเป็นตัวแทนของ bootstrap class loader วิธีนี้จะคืนค่า null ในการใช้งานเช่นถ้าคลาสนี้ถูกโหลดโดย bootstrap class loader"

อาจเป็นเพราะคุณใช้ WildFly และเว็บแอปพลิเคชันของคุณลองทำสิ่งนี้

request.getServletContext().getResource()ส่งคืน URL ทรัพยากร นี่คำขอเป็นวัตถุของ ServletRequest


-2

โค้ดด้านล่างใช้ได้กับ Spring boot (kotlin):

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