Tệp bên trong lọ không hiển thị cho mùa xuân


103

Tất cả

Tôi đã tạo một tệp jar với MANIFEST.MF sau bên trong:

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

Trong thư mục gốc của nó có một tệp được gọi là my.config được tham chiếu trong spring-context.xml của tôi như thế này:

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

Nếu tôi chạy jar, mọi thứ đều ổn, trừ việc tải tệp cụ thể đó:

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
  • các lớp được tải từ bên trong jar
  • mùa xuân và các phụ thuộc khác được tải từ các lọ riêng biệt
  • ngữ cảnh mùa xuân được tải (ClassPathXmlApplicationContext mới ("spring-context / applicationContext.xml"))
  • my.properties được tải vào PropertyPlaceholderConfigurer ("classpath: my.properties")
  • nếu tôi đặt tệp .config của mình bên ngoài hệ thống tệp và thay đổi url tài nguyên thành 'tệp:', mọi thứ dường như ổn ...

Bất kỳ lời khuyên?

Câu trả lời:


211

Nếu các tệp spring-context.xml và my.config của bạn nằm trong các lọ khác nhau thì bạn sẽ cần sử dụng classpath*:my.config?

Thông tin thêm tại đây

Ngoài ra, hãy đảm bảo rằng bạn resource.getInputStream()không sử dụng resource.getFile()khi tải từ bên trong tệp jar.


1
Họ đang ở trong lọ giống nhau, nhưng tôi đã cố gắng giải pháp của bạn với kết quả tương tự: java.io.FileNotFoundException: lớp đường nguồn [classpath *: my.config] không thể được giải quyết để URL vì nó không tồn tại
BTakacs

14
Nhìn lại, một số mã gọi điện của bạn (có thể là BeanConfigurationFactoryBean) đang cố tải một tệp java.io.File. Tệp đề cập đến các tệp trên hệ thống tệp, mà các mục nhập trong một jar thì không. Thay vào đó, mã gọi phải sử dụng resource.getInputStream để tải ra khỏi bình.
sbk

57
... và ĐÂY là câu trả lời ... Cảm ơn! Bên trong một cái lọ không sử dụng resource.getFile () :-)
BTakacs

2
bất kỳ cơ hội nào có một "tại sao?" đằng sau việc không sử dụng getFile () bên trong một cái lọ? Nó chỉ đơn giản là tệp nằm bên trong Jar và do đó "tệp" là tệp jar ??
RockMeetHardplace

8
Đó là nó. Một java.io.File đại diện cho một tệp trên hệ thống tệp, trong cấu trúc thư mục. Jar là một tệp java.io.File. Nhưng bất kỳ thứ gì trong tệp đó đều nằm ngoài tầm với của java.io.File. Đối với java, cho đến khi nó được giải nén, một lớp trong tệp jar không khác gì một từ trong tài liệu word.
sbk

50

Tôi biết câu hỏi này đã được trả lời. Tuy nhiên, đối với những người sử dụng khởi động lò xo, liên kết này đã giúp tôi - https://smarterco.de/java-load-file-classpath-spring-boot/

Tuy nhiên, điều resourceLoader.getResource("classpath:file.txt").getFile();đã gây ra vấn đề này và nhận xét của sbk:

Đó là nó. Một java.io.File đại diện cho một tệp trên hệ thống tệp, trong cấu trúc thư mục. Jar là một tệp java.io.File. Nhưng bất kỳ thứ gì trong tệp đó đều nằm ngoài tầm với của java.io.File. Đối với java, cho đến khi nó được giải nén, một lớp trong tệp jar không khác gì một từ trong tài liệu word.

đã giúp tôi hiểu tại sao phải sử dụng getInputStream()thay thế. Nó hoạt động cho tôi bây giờ!

Cảm ơn!


37

Trong gói Spring jar, tôi sử dụng new ClassPathResource(filename).getFile(), ném ngoại lệ:

không thể được giải quyết thành đường dẫn tệp tuyệt đối vì nó không nằm trong hệ thống tệp: jar

Nhưng sử dụng mới ClassPathResource(filename).getInputStream()sẽ giải quyết vấn đề này. Lý do là tệp cấu hình trong jar không tồn tại trong cây tệp của hệ điều hành, vì vậy phải sử dụng getInputStream().


2

Tôi đã gặp vấn đề tương tự khi sử dụng Tomcat6.x và không có lời khuyên nào mà tôi tìm thấy giúp ích được. Cuối cùng tôi đã xóawork thư mục (của Tomcat) và sự cố đã biến mất.

Tôi biết nó là phi logic nhưng vì mục đích tài liệu ...


1

Theo ý kiến ​​của tôi, câu trả lời của @sbk là cách chúng ta nên làm trong môi trường khởi động mùa xuân (ngoại trừ @Value ("$ {classpath *:})). Nhưng trong trường hợp của tôi, nó không hoạt động nếu thực thi từ độc lập jar..có lẽ tôi đã làm sai điều gì đó.

Nhưng đây có thể là một cách khác để làm điều này,

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

1

Tôi gặp sự cố phức tạp hơn vì tôi có nhiều tệp có cùng tên, một tệp nằm trong lọ Spring Boot chính và các tệp khác nằm trong lọ bên trong lọ mỡ chính. Giải pháp của tôi là lấy tất cả các tài nguyên có cùng tên và sau đó lấy tài nguyên tôi cần lọc theo tên gói. Để lấy tất cả các tệp:

ResourceLoader resourceLoader = new FileSystemResourceLoader();
final Enumeration<URL> systemResources = resourceLoader.getClassLoader().getResources(fileNameWithoutExt + FILE_EXT);

0

Tôi gặp sự cố khi tải đệ quy tài nguyên trong ứng dụng Spring của mình và nhận thấy rằng vấn đề là tôi nên sử dụng resource.getInputStream. Đây là một ví dụ cho thấy cách đọc đệ quy trong tất cả các tệp trong config/myfilesđó là jsontệp.

Ví dụ.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;
  }
}

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.