ไม่พบทรัพยากร Classpath เมื่อรันเป็น jar


131

มีปัญหานี้ทั้งใน Spring Boot 1.1.5 และ 1.1.6 - ฉันกำลังโหลดทรัพยากร classpath โดยใช้คำอธิบายประกอบ @Value ซึ่งใช้งานได้ดีเมื่อฉันเรียกใช้แอปพลิเคชันจากภายใน STS (3.6.0, Windows) อย่างไรก็ตามเมื่อฉันเรียกใช้แพ็คเกจ mvn แล้วลองรัน jar ฉันได้รับข้อยกเว้น FileNotFound

ทรัพยากร message.txt อยู่ใน src / main / resources ฉันได้ตรวจสอบ jar และตรวจสอบแล้วว่ามีไฟล์ "message.txt" ที่ระดับบนสุด (ระดับเดียวกับ application.properties)

นี่คือแอปพลิเคชัน:

@Configuration
@ComponentScan
@EnableAutoConfiguration
public class Application implements CommandLineRunner {

    private static final Logger logger = Logger.getLogger(Application.class);

    @Value("${message.file}")
    private Resource messageResource;

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Override
    public void run(String... arg0) throws Exception {
        // both of these work when running as Spring boot app from STS, but
        // fail after mvn package, and then running as java -jar
        testResource(new ClassPathResource("message.txt"));
        testResource(this.messageResource);
    }

    private void testResource(Resource resource) {
        try {
            resource.getFile();
            logger.debug("Found the resource " + resource.getFilename());
        } catch (IOException ex) {
            logger.error(ex.toString());
        }
    }
}

ข้อยกเว้น:

c:\Users\glyoder\Documents\workspace-sts-3.5.1.RELEASE\classpath-resource-proble
m\target>java -jar demo-0.0.1-SNAPSHOT.jar

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.1.5.RELEASE)

2014-09-16 08:46:34.635  INFO 5976 --- [           main] demo.Application
                  : Starting Application on 8W59XV1 with PID 5976 (C:\Users\glyo
der\Documents\workspace-sts-3.5.1.RELEASE\classpath-resource-problem\target\demo
-0.0.1-SNAPSHOT.jar started by glyoder in c:\Users\glyoder\Documents\workspace-s
ts-3.5.1.RELEASE\classpath-resource-problem\target)
2014-09-16 08:46:34.640 DEBUG 5976 --- [           main] demo.Application
                  : Running with Spring Boot v1.1.5.RELEASE, Spring v4.0.6.RELEA
SE
2014-09-16 08:46:34.681  INFO 5976 --- [           main] s.c.a.AnnotationConfigA
pplicationContext : Refreshing org.springframework.context.annotation.Annotation
ConfigApplicationContext@1c77b086: startup date [Tue Sep 16 08:46:34 EDT 2014];
root of context hierarchy
2014-09-16 08:46:35.196  INFO 5976 --- [           main] o.s.j.e.a.AnnotationMBe
anExporter        : Registering beans for JMX exposure on startup
2014-09-16 08:46:35.210 ERROR 5976 --- [           main] demo.Application
                  : java.io.FileNotFoundException: class path resource [message.
txt] cannot be resolved to absolute file path because it does not reside in the
file system: jar:file:/C:/Users/glyoder/Documents/workspace-sts-3.5.1.RELEASE/cl
asspath-resource-problem/target/demo-0.0.1-SNAPSHOT.jar!/message.txt
2014-09-16 08:46:35.211 ERROR 5976 --- [           main] demo.Application
                  : java.io.FileNotFoundException: class path resource [message.
txt] cannot be resolved to absolute file path because it does not reside in the
file system: jar:file:/C:/Users/glyoder/Documents/workspace-sts-3.5.1.RELEASE/cl
asspath-resource-problem/target/demo-0.0.1-SNAPSHOT.jar!/message.txt
2014-09-16 08:46:35.215  INFO 5976 --- [           main] demo.Application
                  : Started Application in 0.965 seconds (JVM running for 1.435)

2014-09-16 08:46:35.217  INFO 5976 --- [       Thread-2] s.c.a.AnnotationConfigA
pplicationContext : Closing org.springframework.context.annotation.AnnotationCon
figApplicationContext@1c77b086: startup date [Tue Sep 16 08:46:34 EDT 2014]; roo
t of context hierarchy
2014-09-16 08:46:35.218  INFO 5976 --- [       Thread-2] o.s.j.e.a.AnnotationMBe
anExporter        : Unregistering JMX-exposed beans on shutdown

คำตอบ:


203

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


6
ในกรณีของฉันฉันต้องระบุพา ธ ไปยังไฟล์ที่เก็บคีย์ (เพื่อประโยชน์ของตัวเชื่อมต่อ tomcat https) ฉันต้องการห่อไฟล์ jks ไว้ใน jar ของฉัน ตามวิธีการแก้ปัญหาข้างต้นเป็นไปไม่ได้ คุณคุ้นเคยกับวิธีการเพิ่มเติมหรือไม่?
Modi

1
ฉันมีข้อกำหนดที่คล้ายกันมากและไม่มี apis ที่ให้คุณตั้งค่าที่เก็บคีย์เป็นส่วนหนึ่งของ jar @ โมดี้คุณพอจะหาวิธีแก้ปัญหาได้ไหม?
Robin Varghese

คุณหมายถึงกรณีการใช้งานที่เก็บกุญแจหรือไม่? คุณใช้ Spring Boot กับ Tomcat หรือไม่?
Modi

@RobinVarghese คุณพบวิธีแก้ปัญหาของคุณแล้ว ฉันมีกรณีการใช้งานที่คล้ายกันที่ต้องแก้ไข ชื่นชมหากคุณมีข้อเสนอแนะ
Horizon7

ในกรณีที่ Tomcat ต้องการตำแหน่งที่เก็บคีย์เพื่อเปิดใช้ https เราสามารถใช้classpath:filenameเพื่อให้สามารถอ่านไฟล์ที่เก็บคีย์จากภายใน jar ได้
Horizon7

61

หากคุณใช้ Spring framework การอ่านClassPathResourcea Stringนั้นค่อนข้างง่ายโดยใช้Spring framework'sFileCopyUtils :

String data = "";
ClassPathResource cpr = new ClassPathResource("static/file.txt");
try {
    byte[] bdata = FileCopyUtils.copyToByteArray(cpr.getInputStream());
    data = new String(bdata, StandardCharsets.UTF_8);
} catch (IOException e) {
    LOG.warn("IOException", e);
}

มีข้อ จำกัด เกี่ยวกับตำแหน่งที่ไฟล์ต้องนั่งหรือไม่? สามารถอยู่ที่ใดก็ได้ใน classpath หรือไม่? ที่รากของโครงการได้หรือไม่?
Stephane

สามารถอยู่ที่ใดก็ได้ใน classpath
anubhava

45

หากคุณต้องการใช้ไฟล์:

ClassPathResource classPathResource = new ClassPathResource("static/something.txt");

InputStream inputStream = classPathResource.getInputStream();
File somethingFile = File.createTempFile("test", ".txt");
try {
    FileUtils.copyInputStreamToFile(inputStream, somethingFile);
} finally {
    IOUtils.closeQuietly(inputStream);
}

7

เมื่อโครงการ spring boot ทำงานเป็น jar และต้องการอ่านไฟล์บางไฟล์ใน classpath ฉันจะใช้งานตามโค้ดด้านล่าง

Resource resource = new ClassPathResource("data.sql");
BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream()));
reader.lines().forEach(System.out::println);

3

ฉันสร้างคลาส ClassPathResourceReader ด้วยวิธี java 8 เพื่อสร้างไฟล์ที่อ่านง่ายจาก classpath

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.stream.Collectors;

import org.springframework.core.io.ClassPathResource;

public final class ClassPathResourceReader {

    private final String path;

    private String content;

    public ClassPathResourceReader(String path) {
        this.path = path;
    }

    public String getContent() {
        if (content == null) {
            try {
                ClassPathResource resource = new ClassPathResource(path);
                BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream()));
                content = reader.lines().collect(Collectors.joining("\n"));
                reader.close();
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }
        }
        return content;
    }
}

การใช้ประโยชน์:

String content = new ClassPathResourceReader("data.sql").getContent();

2

ฉันพบข้อ จำกัด นี้เช่นกันและสร้างไลบรารีนี้เพื่อเอาชนะปัญหา: spring-boot-jar-resources โดยพื้นฐานแล้วจะช่วยให้คุณสามารถลงทะเบียน ResourceLoader แบบกำหนดเองด้วย Spring Boot ที่แยกทรัพยากร classpath จาก JAR ได้ตามต้องการอย่างโปร่งใส


2
to get list of data from src/main/resources/data folder --
first of all mention your folder location in properties file as - 
resourceLoader.file.location=data

inside class declare your location. 

@Value("${resourceLoader.file.location}")
    @Setter
    private String location;

    private final ResourceLoader resourceLoader;

public void readallfilesfromresources() {
       Resource[] resources;

        try {
            resources = ResourcePatternUtils.getResourcePatternResolver(resourceLoader).getResources("classpath:" + location + "/*.json");
            for (int i = 0; i < resources.length; i++) {
                try {
                InputStream is = resources[i].getInputStream();
                byte[] encoded = IOUtils.toByteArray(is);
                String content = new String(encoded, Charset.forName("UTF-8"));
                }
            }
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
}

1

เจอร์ซีย์จำเป็นต้องแกะขวด

<build>  
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <requiresUnpack>
                    <dependency>
                        <groupId>com.myapp</groupId>
                        <artifactId>rest-api</artifactId>
                    </dependency>
                </requiresUnpack>
            </configuration>
        </plugin>
    </plugins>
</build>  

0
in spring boot :

1) if your file is ouside jar you can use :        

@Autowired
private ResourceLoader resourceLoader;

**.resource(resourceLoader.getResource("file:/path_to_your_file"))**

2) if your file is inside resources of jar you can `enter code here`use :

**.resource(new ClassPathResource("file_name"))**

0

จากคำตอบของ Andy ฉันใช้สิ่งต่อไปนี้เพื่อรับอินพุตสตรีมของ YAML ทั้งหมดภายใต้ไดเร็กทอรีและไดเร็กทอรีย่อยในทรัพยากร (โปรดทราบว่าเส้นทางที่ผ่านไม่ได้ขึ้นต้นด้วย/):

private static Stream<InputStream> getInputStreamsFromClasspath(
        String path,
        PathMatchingResourcePatternResolver resolver
) {
    try {
        return Arrays.stream(resolver.getResources("/" + path + "/**/*.yaml"))
                .filter(Resource::exists)
                .map(resource -> {
                    try {
                        return resource.getInputStream();
                    } catch (IOException e) {
                        return null;
                    }
                })
                .filter(Objects::nonNull);
    } catch (IOException e) {
        logger.error("Failed to get definitions from directory {}", path, e);
        return Stream.of();
    }
}

0

ฉันยังติดอยู่ในรูปแบบที่คล้ายกัน แต่ไม่เหมือนกันทุกประการต้องการอ่านไฟล์ JSON จากโฟลเดอร์ทรัพยากร rc / main / resources ดังนั้นจึงเขียนโค้ดแบบนี้ด้านล่าง

มีหลายวิธีที่ระบุไว้ที่นี่เพื่ออ่านไฟล์ f rom classpath ใน Spring Boot Application

@Value ("classpath: test.json") ทรัพยากรทรัพยากรส่วนตัว

File file = resource.getFile();

@Value("classpath:test.json")

ทรัพยากรทรัพยากรส่วนตัว

File file = resource.getFile();

ตอนนี้รหัสนี้ใช้งานได้ดีเมื่อฉันรันโดยใช้ mvn: spring-boot: run แต่ทันทีที่ฉันสร้างและบรรจุแอปพลิเคชันโดยใช้ maven และเรียกใช้เป็น jar ที่ปฏิบัติการได้ง่ายฉันได้รับข้อยกเว้น มาดูวิธีแก้ปัญหากันเลย

การใช้ InputStream คือสิ่งที่ฉันพบคำตอบในกรณีของฉัน: -

@Value("classpath:test.json")
    Resource resourceFile;

private void testResource(Resource resource) {
        try {
            InputStream resourcee = resource.getInputStream();
            String text = null;
            try (final Reader reader = new InputStreamReader(resourcee)) {
                text = CharStreams.toString(reader);
            }
            System.out.println(text);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

Spring ทำงานบน Concept of Fat Jar ดังนั้นจึงเป็นเรื่องยากมากเนื่องจากทำงานแตกต่างกันใน Eclipse และในขณะที่คุณวิ่งเป็นโถ


0

เกี่ยวกับข้อความแสดงข้อผิดพลาดเดิม

ไม่สามารถแก้ไขเป็นพา ธ ไฟล์แบบสัมบูรณ์ได้เนื่องจากไม่ได้อยู่ในระบบไฟล์

รหัสต่อไปนี้อาจเป็นประโยชน์ในการค้นหาวิธีแก้ปัญหาเส้นทาง:

Paths.get("message.txt").toAbsolutePath().toString();

ด้วยวิธีนี้คุณสามารถระบุได้ว่าแอปพลิเคชันคาดว่าไฟล์หายไปไหน คุณสามารถดำเนินการได้ในวิธีการหลักของแอปพลิเคชันของคุณ

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