简体   繁体   中英

FileNotFoundException exception when reading from a HTML resource

I try to open and read an HTML file from within class path.

Please find the directory structure in screenshot below

在此处输入图像描述

Inside class SendEmail class I want to read that verification.html file.

Code

When using the code below, it is throwing me a java.io.FileNotFoundException exception here:

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

The readHTMLFile method looks like this:

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

However, when I use an absolute path everything is working fine.

I am very new to Java world. Please help me to fix this.

  1. verification.html looks rather like a "class path resource" than a file... (A file is very environment dependent (eg thinking of its path/location), whereas a "CPR" we package & supply with our application & can refer to it with a known&fixed (absolute or relative) (class path) address.

  2. Nor nor (by default) "includes" anything else from src/main/java than *.java files. So please move the according files (including structure/packages) to src/main/resources (or src/test/... accordingly).

  3. When the resource is finally in classpath, since spring:3.2.2, we can do that:

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

    (..also outside/before spring-boot-application.)

    In spring context, the Resource ( Classpath -, ServletContext -, File (,)-, URL -, ...) can also be "injected", like:

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

    ..instead of calling the constructor.

See also:


When you need to refer to verification.html as a File , then please ensure: It has a distinct (absolute (ok!) or relative (good luck!)) address (in all target environments)!

Files and resources in Java

Your file is located inside the classpath . This is a special location within your source-code (here in package utils.emailTemplate.EmailVerificationTemplate ). So we call it a classpath resource or simply resource .

Classpath resources

Usually those resources are destined to be published with your code, although they are actually not code.

In the Maven standard directory layout you would put them inside the special src/main/resources folder, separated from code.

Locating and reading resources

Resources are located relative from classpath using the classpath: schema. Since they are part of the sources, package-tree you can also locate them relative to one of your classes.

From your SendEmail class, the given template has relative path ../ . So you can instantiate it as Resource building the URL using this.getClass().getResource(Stirng relativePath) from within your SendEmail class:

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

Load a resource as input-stream getResourceAsStream(String name) using the relative path from inside your class.

Alternative using Spring's special-purpose extension 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;
    }

Attention: This reading from a file works only if your resource is inside the file system. Not if your resource is inside a JAR:

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

A safer and more robust way to read from the ClassPathResource is resource.getInputStream() .

From InputStream to String

To fix your method, you could simply exchange the File related parts to 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();
}

Or even simpler (see Baeldung's tutorial linked below):

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

Then re-use it to read from any stream (eg a File , a Resource , ClassPathResource , even a URL ). For example:

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

See also

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM