Sumber daya classpath tidak ditemukan ketika berjalan sebagai jar

128

Mengalami masalah ini di Spring Boot 1.1.5 dan 1.1.6 - Saya memuat sumber daya classpath menggunakan anotasi @Value, yang berfungsi dengan baik ketika saya menjalankan aplikasi dari dalam STS (3.6.0, Windows). Namun, ketika saya menjalankan paket mvn dan kemudian mencoba menjalankan toples, saya mendapatkan pengecualian FileNotFound.

Sumber daya, message.txt, ada di src / main / resources. Saya telah memeriksa toples dan memverifikasi bahwa itu berisi file "message.txt" di tingkat atas (tingkat yang sama dengan application.properties).

Inilah aplikasinya:

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

Pengecualian:

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
gyoder
sumber

Jawaban:

201

resource.getFile()mengharapkan sumber daya itu sendiri tersedia di sistem file, yaitu tidak dapat bersarang di dalam file jar. Inilah mengapa ini bekerja ketika Anda menjalankan aplikasi Anda di STS tetapi tidak berfungsi setelah Anda membangun aplikasi Anda dan menjalankannya dari toples yang dapat dieksekusi. Daripada menggunakan getFile()untuk mengakses konten sumber daya, saya akan merekomendasikan menggunakan getInputStream()sebagai gantinya. Itu akan memungkinkan Anda untuk membaca konten sumber daya di mana pun lokasinya.

Andy Wilkinson
sumber
6
Dalam kasus saya, saya perlu memberikan path ke file keystore (demi konektor https tomcat). Saya ingin membungkus file jks di dalam toples saya. Menurut solusi di atas itu tidak mungkin. Apakah Anda terbiasa dengan cara tambahan untuk melakukannya?
Modi
1
Saya memiliki persyaratan yang sangat mirip dan tidak ada apis yang memungkinkan Anda mengatur keystore sebagai bagian dari toples. @Modi, apakah Anda dapat menemukan solusi?
Robin Varghese
Apakah maksud Anda untuk kasing toko kunci? Apakah Anda menggunakan Spring Boot dengan Tomcat?
Modi
@RobinVarghese Anda menemukan solusi untuk masalah Anda. Saya memiliki kasus penggunaan serupa yang harus diselesaikan. Hargai jika Anda memiliki saran.
horizon7
Dalam hal jika kucing jantan membutuhkan lokasi keystore untuk mengaktifkan https, seseorang dapat menggunakan classpath:filenamesehingga file keystore dapat dibaca dari dalam toples.
horizon7
60

Jika Anda menggunakan kerangka kerja Spring maka membaca ClassPathResourceke dalam Stringcukup sederhana menggunakan kerangka SpringFileCopyUtils :

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);
}
anubhava
sumber
Apakah ada kendala di mana file harus duduk? Bisakah itu di mana saja di classpath? Bisakah itu menjadi akar proyek?
Stephane
Itu bisa di mana saja di classpath.
anubhava
45

Jika Anda ingin menggunakan file:

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);
}
Jae-il Lee
sumber
7

ketika proyek boot spring berjalan sebagai toples dan perlu membaca beberapa file di classpath, saya menerapkannya dengan kode di bawah ini

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

Saya telah membuat kelas ClassPathResourceReader dengan cara java 8 untuk membuat file yang mudah dibaca dari 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;
    }
}

Pemanfaatan:

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

Saya menemukan batasan ini juga dan membuat perpustakaan ini untuk mengatasi masalah ini: spring-boot-jar-resources Pada dasarnya Anda dapat mendaftarkan customLoader kustom dengan Spring Boot yang mengekstrak sumber daya classpath dari JAR sesuai kebutuhan, secara transparan.

Ulises
sumber
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);
        }
}
Jayshree Pancholi
sumber
1

Jersey perlu botol yang belum dibuka.

<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>  
utkusonmez
sumber
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"))**
Merzouk MENHOUR
sumber
0

Berdasarkan jawaban Andy, saya menggunakan yang berikut untuk mendapatkan aliran input dari semua YAML di bawah direktori dan sub-direktori dalam sumber daya (Perhatikan bahwa jalur yang dilewati tidak dimulai dengan /):

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();
    }
}
aksh1618
sumber
0

Saya juga terjebak dalam situasi yang sama tetapi tidak persis sama, ingin membaca file JSON dari folder resources.src / main / resources Karena itu tuliskan kode seperti ini di bawah ini.

Ada berbagai Cara yang tercantum di sini untuk membaca file f classpath di Spring Boot Application.

@Value ("classpath: test.json") sumber daya sumber daya pribadi;

File file = resource.getFile();

@Value("classpath:test.json")

sumber daya swasta;

File file = resource.getFile();

Sekarang, kode ini berfungsi dengan baik begitu saya menjalankannya menggunakan, mvn: spring-boot: run tetapi segera setelah saya membangun dan mengemas aplikasi menggunakan maven dan menjalankannya sebagai toples yang dapat dieksekusi sederhana saya mendapatkan pengecualian. Ayo maju dan cari solusinya sekarang.

Menggunakan InputStream adalah apa yang saya temukan jawabannya dalam kasus saya: -

@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 Bekerja pada Konsep Jar Gendut, karena itu sangat Sulit karena berperilaku berbeda di Eclipse dan saat Anda menjalankan sebagai guci.

Sanjay-Dev
sumber
0

Mengenai pesan kesalahan awalnya

tidak dapat diselesaikan ke jalur file absolut karena tidak berada di sistem file

Kode berikut dapat membantu, untuk menemukan solusi untuk masalah jalur:

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

Dengan ini Anda dapat menentukan, di mana aplikasi mengharapkan file yang hilang. Anda dapat menjalankan ini dalam metode utama aplikasi Anda.

SJX
sumber