jar로 실행할 때 클래스 경로 리소스를 찾을 수 없습니다.


128

Spring Boot 1.1.5 및 1.1.6 모두에서이 문제가 발생했습니다. STS (3.6.0, Windows) 내에서 애플리케이션을 실행할 때 잘 작동하는 @Value 주석을 사용하여 클래스 경로 리소스를로드하고 있습니다. 그러나 mvn 패키지를 실행 한 다음 jar를 실행하려고하면 FileNotFound 예외가 발생합니다.

리소스 message.txt는 src / main / resources에 있습니다. jar를 검사하고 최상위 수준 (application.properties와 동일한 수준)에 "message.txt"파일이 포함되어 있는지 확인했습니다.

응용 프로그램은 다음과 같습니다.

@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

답변:


201

resource.getFile()리소스 자체가 파일 시스템에서 사용 가능할 것으로 예상합니다. 즉, jar 파일 내에 중첩 될 수 없습니다. 이것이 STS에서 애플리케이션을 실행할 때 작동하지만 애플리케이션을 빌드하고 실행 가능한 jar에서 실행하면 작동하지 않는 이유입니다. 오히려 사용하는 것보다 getFile()자원의 내용에 액세스하려면, 내가 사용하는 것이 좋습니다 것 getInputStream()대신. 그러면 리소스의 위치에 관계없이 리소스의 콘텐츠를 읽을 수 있습니다.


6
제 경우에는 (톰캣 https 커넥터를 위해) 키 스토어 파일에 대한 경로를 제공해야합니다. jks 파일을 제 항아리 안에 래핑하고 싶습니다. 위의 솔루션에 따르면 불가능합니다. 추가 방법에 대해 알고 있습니까?
Modi 2014 년

1
필자는 매우 유사한 요구 사항이 있으며 항아리의 일부로 키 저장소를 설정할 수있는 API가 없습니다. @Modi, 해결책을 찾을 수 있었습니까?
Robin Varghese 2015 년

키 저장소 사용 사례를 의미합니까? Tomcat과 함께 Spring Boot를 사용하고 있습니까?
Modi 2015 년

@RobinVarghese는 문제에 대한 해결책을 찾았습니까? 해결해야 할 유사한 사용 사례가 있습니다. 제안 사항이 있으면 감사하십시오.
horizon7

tomcat이 https를 활성화하기 위해 키 저장소 위치가 필요한 경우 classpath:filenamejar 내에서 키 저장소 파일을 읽을 수 있도록 사용할 수 있습니다.
horizon7

60

당신은 스프링 프레임 워크를 사용하는 경우 다음 읽기 ClassPathResource로하는 String사용하여 아주 간단 스프링 프레임 워크의를FileCopyUtils :

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);
}

파일이 있어야하는 위치에 대한 제약이 있습니까? 클래스 경로 어디에나있을 수 있습니까? 프로젝트의 루트에있을 수 있습니까?
Stephane

클래스 경로의 모든 위치에있을 수 있습니다.
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

스프링 부트 프로젝트가 항아리로 실행되고 클래스 경로에서 일부 파일을 읽어야 할 때 아래 코드로 구현합니다.

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

3

클래스 경로에서 파일을 쉽게 읽을 수 있도록 Java 8 방식으로 ClassPathResourceReader 클래스를 만들었습니다.

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 기본적으로 필요에 따라 JAR에서 클래스 경로 리소스를 투명하게 추출하는 Spring Boot에 사용자 지정 ResourceLoader를 등록 할 수 있습니다.


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 파일을 읽고 싶었습니다 .src / main / resources 따라서 아래와 같은 코드를 작성했습니다.

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은 Fat Jar의 개념에 대해 작동하므로 Eclipse에서 다르게 동작하고 jar로 실행하는 동안 실제로 작동하기가 어렵습니다.


0

원래 오류 메시지에 관하여

파일 시스템에 상주하지 않기 때문에 절대 파일 경로로 확인할 수 없습니다.

다음 코드는 경로 문제에 대한 해결책을 찾는 데 도움이 될 수 있습니다.

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

이를 통해 응용 프로그램이 누락 된 파일을 예상하는 위치를 확인할 수 있습니다. 애플리케이션의 기본 메소드에서 실행할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.