作为 jar 运行时找不到类路径资源

在 Spring Boot 1.1.5和1.1.6中都有这个问题——我正在使用@Value 注释加载类路径资源,当我在 STS (3.6.0,Windows)中运行应用程序时,这个注释工作得很好。但是,当我运行一个 mvn 包然后尝试运行 jar 时,会得到 FileNotfound 异常。

Txt 资源位于 src/main/resources 中。我已经检查了 jar,并确认它在顶层(与 application.properties 相同的级别)包含文件“ message.txt”。

这是申请表:

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

例外:

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
227552 次浏览

resource.getFile() 期望资源本身在文件系统上可用,也就是说,它不能嵌套在 jar 文件中。这就是为什么当您在 STS (Spring Tool Suite)中运行应用程序时它会工作,但是当您构建了应用程序并从可执行 jar 中运行它时它就不工作了。与其使用 getFile()访问资源的内容,不如建议使用 getInputStream()。这将允许您读取资源的内容,而不管它位于何处。

如果要使用文件:

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

如果您使用 Spring 框架,那么使用 Spring 框架的 FileCopyUtilsClassPathResource读入 String是相当简单的:

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

我也遇到了这个限制,并创建了这个库来克服这个问题: 它基本上允许您使用 Spring Boot 注册一个定制的 ResourceLoader,它可以根据需要透明地从 JAR 中提取类路径资源。

当 Spring 引导项目作为 jar 运行并且需要读取类路径中的一些文件时,我通过以下代码实现它

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

新泽西需要打开罐子。

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

我已经用 java 8方法创建了一个 ClassPathResourceReader 类,以便于从类路径读取文件

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

用途:

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

要从 src/main/resources/data文件夹获取数据列表: 首先在属性文件中提到您的文件夹位置 resourceLoader.file.location=data.

然后在教室里宣布你的位置:

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

基于 Andy 的 回答,我使用以下方法获取资源目录和子目录下所有 YAML 的输入流(注意,传递的路径不以 /开头) :

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

关于最初的错误消息

无法解析为绝对文件路径,因为它不驻留在 文件系统

下面的代码有助于找到路径问题的解决方案:

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

通过这种方法,您可以确定应用程序期望丢失的文件位于何处。您可以在应用程序的 main 方法中执行此操作。

我注意到的另一个重要的事情是,当运行应用程序时,它忽略了资源文件夹中的文件/文件夹中的大写字母,在以 jar 形式运行时,它不会忽略这些大写字母。因此,如果您的文件位于 Testfolder/messages.txt下的 resources 文件夹中

@Autowired
ApplicationContext appContext;


// this will work when running the application, but will fail when running as jar
appContext.getResource("classpath:testfolder/message.txt");

因此,不要在你的资源中使用大写字母,也不要在 ClassPathResource 的构造函数中添加这些大写字母:

appContext.getResource("classpath:Testfolder/message.txt");

我也面临着同样的错误

InputStream = new ClassPathResource (“ filename.ext”) . inputStream () ;

这应该可以在运行时解决 FileNotFoundException

这在静态环境中对我有效:

    InputStream inputStream = ClassName.class.getClassLoader().getResourceAsStream("folderName/fileName.xml");
Reader reader = new InputStreamReader(inputStream);
String xml = CharStreams.toString(reader);

根据接受的答案,答案是正确的。我给我的解决方案读取流到字符串更有效的 (使用 StreamUtils),而不是尝试创建一个临时文件和读取文本。

  @Bean
public String readFromResource(final @Value("classpath:data/message.txt") Resource messageFile)
throws IOException {
return StreamUtils.copyToString(messageFile.getInputStream(), StandardCharsets.UTF_8);
}

我在使用“ java-jar < file _ name”运行时遇到过同样的问题。罐子”。当我运行它在 STS 的工作正常。 下面一行帮助我解决了这个问题 String templateFilePath = “/template/Itemized _ Report _ 2021 _ V1. xlsx”; InputStream resource ceFile = * * getClass () . getResourceAsStream (templateFilePath)

Excel < strong > report file template path./template/Itemized _ Report _ 2021 _ V1. xlsx 异常: D: STS% 20WORKSPACE app 目标类模板 Itemized _ Report _ 2021 _ V1. xlsx (系统无法找到指定的路径)

解决方案: @ 重写 Public void exportExcelReport (HttpServletResponse,List context) 抛出 IOException,URISyntaxException { String templateFilePath = “/template/Itemized _ Report _ 2021 _ V1. xlsx”; InputStream 资源文件 = GetClass () . getResourceAsStream (templateFilePath); 如果(ourceFile = = null){ 抛出新的 IllegalArgumentException (“ Template file not found!”+ templateFilePath) ; { ServletOutputStream outputStream = null; 尝试{ Info (“合并报告与能力 Excel 报告文件模板路径{}”,templateFilePath) ; Workbook = new XSSFWorkbook (resources) ; 准备能力报告(上下文) ; (上下文) ;

            // Return as octet-stream format in the rest response
outputStream = response.getOutputStream();
workbook.write(outputStream);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
workbook.close();
if (outputStream != null)
outputStream.close();
}
}
}

这可以在一个已经修改过的 Springboot JAR 上工作。

InputStream is = new ClassPathResource("randomlists/names.txt").getInputStream();
Scanner s = new Scanner(is);
ArrayList<String> names = new ArrayList<>();
while (s.hasNext()){
names.add(s.next());
}
s.close();

扫描器有带 inputStream 的构造函数。

这是我在 Springboot 中读取静态文本资源文件的方法。