Không tìm thấy tài nguyên classpath khi chạy dưới dạng jar


127

Gặp vấn đề này cả trong Spring Boot 1.1.5 và 1.1.6 - Tôi đang tải tài nguyên đường dẫn bằng cách sử dụng chú thích @Value, hoạt động tốt khi tôi chạy ứng dụng từ bên trong STS (3.6.0, Windows). Tuy nhiên, khi tôi chạy gói mvn và sau đó thử chạy jar, tôi nhận được ngoại lệ FileNotFound.

Tài nguyên, message.txt, nằm trong src / main / resource. Tôi đã kiểm tra jar và xác minh rằng nó chứa tệp "message.txt" ở cấp cao nhất (cùng cấp với application.properations).

Đây là ứng dụng:

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

Sự ngoại lệ:

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

Câu trả lời:


201

resource.getFile()hy vọng tài nguyên sẽ có sẵn trên hệ thống tệp, nghĩa là nó không thể được lồng trong một tệp jar. Đây là lý do tại sao nó hoạt động khi bạn chạy ứng dụng của mình trong STS nhưng không hoạt động khi bạn đã xây dựng ứng dụng của mình và chạy nó từ tệp thực thi. Thay vì sử dụng getFile()để truy cập nội dung của tài nguyên, tôi khuyên bạn nên sử dụng getInputStream()thay thế. Điều đó sẽ cho phép bạn đọc nội dung của tài nguyên bất kể nó nằm ở đâu.


6
Trong trường hợp của tôi, tôi cần cung cấp đường dẫn đến tệp kho khóa (vì mục đích của trình kết nối https tomcat). Tôi muốn bọc tệp jks bên trong tệp jar của mình. Theo giải pháp trên thì không thể. Bạn có quen thuộc với cách bổ sung để làm điều đó?
Modi

1
Tôi có một yêu cầu rất giống nhau và không có apis nào cho phép bạn đặt kho khóa là một phần của bình. @Modi, bạn có thể tìm ra giải pháp nào không?
Robin Varghese

Bạn có nghĩa là cho trường hợp sử dụng cửa hàng quan trọng? Bạn đang sử dụng Spring Boot với Tomcat?
Modi

@RobinVarghese bạn đã tìm thấy bất kỳ giải pháp cho vấn đề của bạn. Tôi có một trường hợp sử dụng tương tự để được giải quyết. Đánh giá cao nếu bạn có một số gợi ý.
chân trời7

Trong trường hợp nếu tomcat cần vị trí kho khóa để bật https, người ta có thể sử dụng classpath:filenameđể tệp kho khóa có thể được đọc từ trong tệp.
chân trời7

60

Nếu bạn đang sử dụng Spring framework thì việc đọc ClassPathResourcevào một Stringkhá đơn giản bằng cách sử dụng Spring frameworkFileCopyUtils :

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

Có một ràng buộc như nơi tập tin phải ngồi? Nó có thể là bất cứ nơi nào trong classpath? Nó có thể là gốc rễ của dự án?
Stephane

Nó có thể là bất cứ nơi nào trong classpath.
anubhava

45

Nếu bạn muốn sử dụng một tập tin:

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

Khi dự án khởi động mùa xuân chạy như một jar và cần đọc một số tệp trong classpath, tôi thực hiện nó bằng mã bên dưới

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

3

Tôi đã tạo một lớp ClassPathResourceReader theo cách java 8 để làm cho các tệp dễ đọc từ 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;
    }
}

Sử dụng:

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

2

Tôi cũng gặp phải giới hạn này và đã tạo thư viện này để khắc phục vấn đề: spring-boot-jar-resource Nó về cơ bản cho phép bạn đăng ký ResourceLoader tùy chỉnh với Spring Boot để trích xuất tài nguyên đường dẫn từ JAR khi cần, trong suốt.


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

Jersey cần phải được đóng gói lọ.

<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

Dựa trên câu trả lời của Andy, tôi đã sử dụng cách sau để nhận luồng đầu vào của tất cả YAML trong một thư mục và thư mục con trong tài nguyên (Lưu ý rằng đường dẫn đi qua không bắt đầu bằng /):

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

Tôi cũng bị mắc kẹt trong tình huống tương tự nhưng không hoàn toàn giống nhau, muốn đọc một tệp JSON từ thư mục tài nguyên.src / main / resource Do đó đã viết một mã như thế này bên dưới.

Có nhiều cách khác nhau được liệt kê ở đây để đọc tệp f rom classpath trong Spring Boot Application.

@Value ("classpath: test.json") tài nguyên tài nguyên riêng;

File file = resource.getFile();

@Value("classpath:test.json")

tài nguyên tư nhân;

File file = resource.getFile();

Bây giờ, mã này hoạt động hoàn toàn tốt khi tôi chạy nó bằng cách sử dụng, mvn: spring-boot: chạy nhưng ngay sau khi tôi xây dựng và đóng gói ứng dụng bằng maven và chạy nó như một tệp thực thi đơn giản, tôi sẽ nhận được một ngoại lệ. Hãy tiếp tục và tìm ra giải pháp ngay bây giờ.

Sử dụng InputStream là những gì tôi tìm thấy câu trả lời trong trường hợp của mình: -

@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 hoạt động dựa trên khái niệm về Fat Jar, do đó nó thực sự khó khăn vì nó hoạt động khác nhau trong Eclipse và trong khi bạn chạy như một cái bình.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.