从 HTML 资源读取时出现 FileNotFoundException 异常

FileNotFoundException exception when reading from a HTML resource

我尝试打开并读取 class 路径中的 HTML 文件。

请在下面的截图中找到目录结构

里面 class SendEmail class 我想读那个 verification.html 文件。

代码

当使用下面的代码时,它向我抛出一个 java.io.FileNotFoundException 异常:

emailContent = readHTMLFile("../emailTemplate/EmailVerificationTemplate/verification.html");

readHTMLFile 方法如下所示:

public String readHTMLFile(String path) throws IOException {
    String emailContent = "";
    StringBuilder stringBuilder = new StringBuilder();
    BufferedReader bufferedReader = new BufferedReader(new FileReader(path));
    while ((emailContent = bufferedReader.readLine()) != null) {
        stringBuilder.append(emailContent);
    }
    return stringBuilder.toString();
}

但是,当我使用 绝对 路径时,一切正常。

我是 Java 世界的新手。 请帮我解决这个问题。

  1. verification.html 看起来更像是“class 路径资源”而不是文件... (一个文件非常依赖于环境(例如考虑它的 path/location),而我们打包并随我们的应用程序一起提供的“CPR”可以用已知和固定的(绝对或相对)引用它(class路径)地址。

  2. 也不 nor (默认情况下)“包含”src/main/java 中除 *.java 文件之外的任何其他内容。所以请将相应的文件(包括structure/packages)移动到src/main/resources(或相应的src/test/...)。

  3. 当资源最终在class路径中时,由于spring:3.2.2,我们可以这样做:

    String emailBody = org.springframework.util.StreamUtils.
      copyToString(
         new org.springframework.core.io.ClassPathResource(
    "/full/package/of/emailTemplate/EmailVerificationTemplate/verification.html")
         .getInputStream(), 
         /* you must know(!), better: */
         Charset.forName("UTF-8")
    );
    

    (..也 outside/before spring-引导应用程序。)

    在 spring 上下文中,ResourceClasspath-、ServletContext-、File(!)-、URL- , ...) 也可以被“注入”, 比如:

    @Value("classpath:/full/package/...")Resource verificationEmailBody
    

    ..而不是调用构造函数。

另请参阅:


当您需要verification.html引用为File时,请确保: 它有一个独特的(绝对(好!)或相对(祝你好运!))地址(在所有目标环境中)!

Java

中的文件和资源

您的文件位于 class 路径 内。这是您的源代码中的一个特殊位置(此处在包 utils.emailTemplate.EmailVerificationTemplate 中)。所以我们称它为 classpath resource 或简称为 resource.

类路径资源

通常那些 resources 注定要与您的代码一起发布,尽管它们实际上不是代码。

Maven standard directory layout 中,您可以将它们放在特殊的 src/main/resources 文件夹中,与代码分开。

查找和阅读资源

资源位于相对于class路径的位置,使用classpath:架构。由于它们是源代码的一部分,您还可以相对于 classes 之一找到它们。

来自您的 SendEmail class,给定的模板具有相对路径 ../。因此,您可以将其实例化为 ResourceSendEmail class:

中使用 this.getClass().getResource(Stirng relativePath) 构建 URL
class SendEmail {
    private final String relativePath = "../emailTemplate/EmailVerificationTemplate/verification.html";

    // build the URL for the resource relative from within your class
    public URL getVerificaitonEmailTemplateUrl() {
        URL templateResourceUrl =  this.getClass().getResource(relativePath);
        return templateResourceUrl;
    }

    // load the resource
    public InputStream getVerificaitonEmailTemplateStream() {
        InputStream is = this.getClass().getResourceAsStream(relativePath);
        return is;
    }
}

加载资源作为输入流getResourceAsStream(String name) 使用 class.

内部的相对路径

使用 Spring 的专用扩展 ClassPathResource 的替代方法:

    private final String relativePath = "../emailTemplate/EmailVerificationTemplate/verification.html";

    public String loadContentAsFile() {
        ClassPathResource resource = new ClassPathResource(relativePath);
        File file resource.getFile();
        String content = new String(Files.readAllBytes(file.toPath()));
        return content;
    }

    public InputStream getContentAsStream() {
        ClassPathResource resource = new ClassPathResource(relativePath);
        InputStream is resource.getInputStream();
        return is;
    }

注意:此从文件读取 仅当您的资源在文件系统中时才有效。如果您的资源在 JAR 中则不会:

This implementation returns a File reference for the underlying class path resource, provided that it refers to a file in the file system.

ClassPathResource 中读取的更安全、更可靠的方法是 resource.getInputStream()

从 InputStream 到 String

要修复您的方法,您只需将 File 相关部分替换为 InputStream:

public String readHTML(InputStream is) throws IOException {
    String emailContent = "";
    StringBuilder stringBuilder = new StringBuilder();
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is));
    while ((emailContent = bufferedReader.readLine()) != null) {
        stringBuilder.append(emailContent);
    }
    return stringBuilder.toString();
}

或者更简单(参见下面链接的 Baeldung 教程):

String text = new BufferedReader(
    new InputStreamReader(inputStream, StandardCharsets.UTF_8))  // choose the encoding to fit
        .lines()
        .collect(Collectors.joining("\n"));

然后重新使用它从任何流中读取(例如 FileResourceClassPathResource,甚至 URL)。例如:

public String loadTemplate(String relativeResourcePath) throws IOException {
    InputStream inputStream = this.getClass().getResourceAsStream(relativeResourcePath)
    String text = new BufferedReader(
      new InputStreamReader(inputStream, StandardCharsets.UTF_8))
        .lines()
        .collect(Collectors.joining("\n"));
    return text;
}

另见