File di dalam toples tidak terlihat untuk pegas

103

Semua

Saya membuat file jar dengan MANIFEST.MF berikut di dalamnya:

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.8.3
Created-By: 1.6.0_25-b06 (Sun Microsystems Inc.)
Main-Class: my.Main
Class-Path: . lib/spring-core-3.2.0.M2.jar lib/spring-beans-3.2.0.M2.jar

Di akarnya ada file bernama my.config yang direferensikan di spring-context.xml saya seperti ini:

<bean id="..." class="...">
    <property name="resource" value="classpath:my.config" />
</bean>

Jika saya menjalankan toples, semuanya terlihat baik-baik saja kecuali pemuatan file khusus itu:

Caused by: java.io.FileNotFoundException: class path resource [my.config] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:/D:/work/my.jar!/my.config
        at org.springframework.util.ResourceUtils.getFile(ResourceUtils.java:205)
    at org.springframework.core.io.AbstractFileResolvingResource.getFile(AbstractFileResolvingResource.java:52)
    at eu.stepman.server.configuration.BeanConfigurationFactoryBean.getObject(BeanConfigurationFactoryBean.java:32)
    at eu.stepman.server.configuration.BeanConfigurationFactoryBean.getObject(BeanConfigurationFactoryBean.java:1)
    at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:142)
    ... 22 more
  • kelas dimuat dari dalam toples
  • pegas dan dependensi lainnya dimuat dari toples terpisah
  • konteks pegas dimuat (ClassPathXmlApplicationContext baru ("spring-context / applicationContext.xml"))
  • my.properties dimuat ke PropertyPlaceholderConfigurer ("classpath: my.properties")
  • jika saya meletakkan file .config saya di luar sistem file, dan mengubah url sumber daya menjadi 'file:', semuanya tampak baik-baik saja ...

Ada tips?

BTakacs
sumber

Jawaban:

211

Jika file spring-context.xml dan my.config Anda berada di jars yang berbeda, maka Anda perlu menggunakan classpath*:my.config?

Info selengkapnya di sini

Juga, pastikan Anda menggunakan resource.getInputStream()bukan resource.getFile()saat memuat dari dalam file jar.

sbk
sumber
1
Mereka berada di jar yang sama, tetapi saya mencoba solusi Anda dengan hasil yang sama: java.io.FileNotFoundException: sumber jalur kelas [classpath *: my.config] tidak dapat diselesaikan ke URL karena tidak ada
BTakacs
14
Melihat lagi, beberapa kode panggilan Anda (mungkin BeanConfigurationFactoryBean) mencoba memuat java.io.File. File mengacu pada file-file di sistem file, yang bukan entri dalam jar. Kode panggilan harus menggunakan resource.getInputStream sebagai gantinya untuk memuat dari jar.
sbk
57
... dan INI adalah jawabannya ... Terima kasih! Di dalam toples jangan gunakan resource.getFile () :-)
BTakacs
2
ada kemungkinan ada pertanyaan "mengapa?" di belakang tidak menggunakan getFile () di dalam botol? Apakah hanya file tersebut di dalam Jar dan karenanya "file" adalah file jar ??
RockMeetHardplace
8
Itu dia. Sebuah java.io.File mewakili file pada sistem file, dalam struktur direktori. The Jar adalah java.io.File. Tapi apa pun di dalam file itu berada di luar jangkauan java.io.File. Sejauh menyangkut java, sampai tidak dikompresi, kelas dalam file jar tidak berbeda dengan kata dalam dokumen kata.
sbk
50

Saya tahu pertanyaan ini telah terjawab. Namun, bagi mereka yang menggunakan boot musim semi, tautan ini membantu saya - https://smarterco.de/java-load-file-classpath-spring-boot/

Namun, resourceLoader.getResource("classpath:file.txt").getFile();penyebabnya adalah masalah ini dan komentar sbk:

Itu dia. Sebuah java.io.File mewakili file pada sistem file, dalam struktur direktori. The Jar adalah java.io.File. Tapi apa pun di dalam file itu berada di luar jangkauan java.io.File. Sejauh menyangkut java, sampai tidak dikompresi, kelas dalam file jar tidak berbeda dengan kata dalam dokumen kata.

membantu saya memahami mengapa menggunakan getInputStream() sebagai gantinya. Ini berhasil untuk saya sekarang!

Terima kasih!

jmathewt
sumber
37

Dalam paket spring jar, saya menggunakan new ClassPathResource(filename).getFile(), yang memunculkan pengecualian:

tidak dapat diselesaikan ke jalur file absolut karena tidak berada dalam sistem file: jar

Tetapi menggunakan yang baru ClassPathResource(filename).getInputStream()akan menyelesaikan masalah ini. Alasannya adalah bahwa file konfigurasi di jar tidak ada di pohon file sistem operasi, jadi harus digunakan getInputStream().

tao zeng
sumber
2

Saya memiliki masalah yang sama saat menggunakan Tomcat6.x dan tidak ada saran yang saya temukan membantu. Pada akhirnya saya menghapus workfolder (dari Tomcat) dan masalahnya hilang.

Saya tahu itu tidak masuk akal tetapi untuk tujuan dokumentasi ...

takacsot
sumber
1

Jawaban oleh @sbk adalah cara yang harus kita lakukan di lingkungan spring-boot (selain dari @Value ("$ {classpath *:})), menurut saya. Tapi dalam skenario saya itu tidak berfungsi jika dijalankan dari standalone jar..mungkin aku melakukan sesuatu yang salah.

Tapi ini bisa menjadi cara lain untuk melakukan ini,

InputStream is = this.getClass().getClassLoader().getResourceAsStream(<relative path of the resource from resource directory>);
Abhishek Chatterjee
sumber
1

Saya mengalami masalah yang lebih kompleks karena saya memiliki lebih dari satu file dengan nama yang sama, satu ada di toples Spring Boot utama dan yang lainnya ada di toples di dalam toples lemak utama. Solusi saya adalah mendapatkan semua sumber daya dengan nama yang sama dan setelah itu mendapatkan yang saya butuhkan untuk memfilter berdasarkan nama paket. Untuk mendapatkan semua file:

ResourceLoader resourceLoader = new FileSystemResourceLoader();
final Enumeration<URL> systemResources = resourceLoader.getClassLoader().getResources(fileNameWithoutExt + FILE_EXT);
Enrique Jiménez Flores
sumber
0

Saya mengalami masalah saat memuat sumber daya secara rekursif di aplikasi Spring saya, dan menemukan bahwa masalahnya adalah yang seharusnya saya gunakan resource.getInputStream. Berikut adalah contoh yang menunjukkan cara membaca secara rekursif di semua file di config/myfilesdalamnyajson file.

Example.java

private String myFilesResourceUrl = "config/myfiles/**/";
private String myFilesResourceExtension = "json";

ResourceLoader rl = new ResourceLoader();

// Recursively get resources that match. 
// Big note: If you decide to iterate over these, 
// use resource.GetResourceAsStream to load the contents
// or use the `readFileResource` of the ResourceLoader class.
Resource[] resources = rl.getResourcesInResourceFolder(myFilesResourceUrl, myFilesResourceExtension);

// Recursively get resource and their contents that match. 
// This loads all the files into memory, so maybe use the same approach 
// as this method, if need be.
Map<Resource,String> contents = rl.getResourceContentsInResourceFolder(myFilesResourceUrl, myFilesResourceExtension);

ResourceLoader.java

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.util.StreamUtils;

public class ResourceLoader {
  public Resource[] getResourcesInResourceFolder(String folder, String extension) {
    ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
    try {
      String resourceUrl = folder + "/*." + extension;
      Resource[] resources = resolver.getResources(resourceUrl);
      return resources;
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  public String readResource(Resource resource) throws IOException {
    try (InputStream stream = resource.getInputStream()) {
      return StreamUtils.copyToString(stream, Charset.defaultCharset());
    }
  }

  public Map<Resource, String> getResourceContentsInResourceFolder(
      String folder, String extension) {
    Resource[] resources = getResourcesInResourceFolder(folder, extension);

    HashMap<Resource, String> result = new HashMap<>();
    for (var resource : resources) {
      try {
        String contents = readResource(resource);
        result.put(resource, contents);
      } catch (IOException e) {
        throw new RuntimeException("Could not load resource=" + resource + ", e=" + e);
      }
    }
    return result;
  }
}
Brad Parks
sumber
0

Saya memiliki masalah yang sama, akhirnya menggunakan Sumber Jambu Biji yang jauh lebih nyaman :

Resources.getResource("my.file")
Ahmad Abdelghany
sumber