Risorsa classpath non trovata durante l'esecuzione come jar


128

Avendo questo problema sia in Spring Boot 1.1.5 che 1.1.6 - Sto caricando una risorsa classpath usando un'annotazione @Value, che funziona perfettamente quando eseguo l'applicazione da STS (3.6.0, Windows). Tuttavia, quando eseguo un pacchetto mvn e quindi provo a eseguire il jar, ottengo eccezioni FileNotFound.

La risorsa, message.txt, è in src / main / resources. Ho ispezionato il vaso e verificato che contiene il file "message.txt" al livello superiore (stesso livello di application.properties).

Ecco l'applicazione:

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

L'eccezione:

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

Risposte:


201

resource.getFile()si aspetta che la risorsa stessa sia disponibile sul file system, cioè non può essere nidificata all'interno di un file jar. Questo è il motivo per cui funziona quando si esegue l'applicazione in STS ma non funziona dopo aver creato l'applicazione ed eseguirla dal vaso eseguibile. Invece di utilizzare getFile()per accedere al contenuto della risorsa, consiglierei getInputStream()invece di usarlo . Ciò ti consentirà di leggere il contenuto della risorsa indipendentemente da dove si trova.


6
Nel mio caso, devo fornire un percorso al file keystore (per il bene di un connettore tomcat https). Voglio avvolgere il file jks nel mio vaso. Secondo la soluzione di cui sopra non è possibile. Conosci un altro modo per farlo?
Modi,

1
Ho un requisito molto simile e non ci sono API che ti consentono di impostare il keystore come parte del vaso. @Modi, sei riuscito a trovare una soluzione?
Robin Varghese,

Intendi per caso d'uso dell'archivio chiavi? Stai usando Spring Boot con Tomcat?
Modi

@RobinVarghese hai trovato una soluzione al tuo problema. Ho un caso d'uso simile da risolvere. Apprezzo se hai qualche suggerimento.
horizon7,

Nel caso in cui tomcat necessiti della posizione del keystore per abilitare https, è possibile utilizzarlo in classpath:filenamemodo che il file del keystore possa essere letto all'interno del jar.
horizon7,

60

Se stai usando Spring framework, leggere ClassPathResourcein a Stringè piuttosto semplice usando 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);
}

Esiste un vincolo su dove deve trovarsi il file? Può essere ovunque nel percorso di classe? Può essere alla radice del progetto?
Stephane,

Può essere ovunque nel percorso di classe.
Anubhava,

45

Se si desidera utilizzare un 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);
}

7

quando il progetto di avvio a molla funziona come un vaso e ho bisogno di leggere alcuni file nel percorso di classe, lo implemento con il codice seguente

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

3

Ho creato una classe ClassPathResourceReader in modo java 8 per rendere facili i file di lettura dal percorso di classe

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

Utilizzo:

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

2

Ho riscontrato anche questa limitazione e ho creato questa libreria per ovviare al problema: spring-boot-jar-resources Fondamentalmente ti consente di registrare un ResourceLoader personalizzato con Spring Boot che estrae le risorse del percorso di classe dal JAR secondo necessità, in modo trasparente.


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 ha bisogno di essere barattoli non imballati.

<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

Sulla base della risposta di Andy ho usato quanto segue per ottenere flussi di input di tutti gli YAML in una directory e sottodirectory nelle risorse (Nota che il percorso passato non inizia con /):

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

Mi sono anche bloccato in una posizione simile ma non esattamente simile, volevo leggere un file JSON dalla cartella folder.src / main / resources Quindi ho scritto un codice simile al seguente.

Esistono vari modi elencati qui per leggere un file dal percorso di classe nell'applicazione Spring Boot.

@Value ("classpath: test.json") risorsa risorsa privata;

File file = resource.getFile();

@Value("classpath:test.json")

risorsa privata;

File file = resource.getFile();

Ora, questo codice funziona perfettamente una volta che lo eseguo usando, mvn: spring-boot: run ma non appena sto costruendo e impacchettando l'applicazione usando Maven ed eseguendolo come semplice vaso eseguibile sto ottenendo un'eccezione. Andiamo avanti e scopriamo la soluzione ora.

Utilizzando InputStream è ciò che ho trovato la risposta nel mio caso: -

@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 funziona sul concetto di Fat Jar, quindi è davvero difficile in quanto si comporta diversamente in Eclipse e mentre corri come un vaso.


0

Per quanto riguarda il messaggio di errore originariamente

non può essere risolto in percorso file assoluto perché non risiede nel file system

Il seguente codice potrebbe essere utile per trovare la soluzione al problema del percorso:

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

Con questo è possibile determinare dove l'applicazione si aspetta il file mancante. È possibile eseguire questo nel metodo principale dell'applicazione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.