Search code examples
javahtmlspring-bootimagethymeleaf

How to add image to html page using Thymeleaf?


I have the problem that my thymeleaf block don`t show image and shortcut icon on html page

I tried it with file path:

<img class="logo" th:src="@{src/main/resources/static/logo.png}" alt="Logo">

and also tried it with rest api:

<img class="logo" th:src="@{/api/v1/logo}" alt="Logo">

there is controller:

@RestController
@RequestMapping("/api/v1/logo")
public class LogoController {

    @GetMapping(produces = MediaType.IMAGE_JPEG_VALUE)
    public Resource getLogo() throws IOException {
        return new ByteArrayResource(Files.toByteArray(new File("src/main/resources/static/logo.png")));
    }
}

And I always get alt instead of image...


Solution

  • Problem 1: Reading the file correctly

    If you're using the default configuration, then anything you put into src/main/resources is copied to the classpath. Because of that, you should nowhere in your code refer to src/main/resources, but to the classpath itself.

    If you run locally, it might still work, but as soon as you run your JAR file somewhere else, it will break entirely.

    So ideally, you should rewrite your controller as:

    // Get location from classpath
    URI location = getClass().getClassLoader().getResource("static/logo.png").toURI();
    // Get file
    Path file = Paths.get(location);
    // Read bytes
    return new ByteArrayResource(Files.readAllBytes(file));
    

    Since retrieving a resource from a file is a common task, you don't really have to read the bytes though. In stead of using ByteArrayResource you can use FileSystemResource:

    // Get location from classpath
    URI location = getClass().getClassLoader().getResource("static/logo.png").toURI();
    // Get file
    Path file = Paths.get(location);
    return new FileSystemResource(file);
    

    You can even make this shorter, because retrieving a resource from the classpath is so common that there is a ClasspathResource class:

    return new ClassPathResource("static/logo.png");
    

    And that's not everything, it happens commonly that you need to serve web resources from the classpath, so in Spring Boot, everything that is in the classpath:static/ folder or classpath:public/ folder is already served on the web. So normally, your image is already available at http://localhost:8080/logo.png without the need for your controller method.

    So normally you can remove that controller method entirely.


    Problem 2: Referring to the file correctly

    This brings us to the second problem. Currently, you're referring to your image either with @{/api/v1/logo} or @{src/main/resources/static/logo.png}. Thymeleaf interpretes @{path/to/file} as a context-relative URL, so the only thing it does is prepend the context path if you have any and expects the file to be available at http://localhost:[serverport]/[contextpath]/path/to/file.

    But as we've established before, your image should be available at http://localhost:8080/logo.png, and thus, you should use @{/logo.png}:

    <img class="logo" th:src="@{/logo.png}" alt="Logo">
    

    If this doesn't work, then:

    • You may have misconfigured your build tool to not include src/main/resources on the classpath.
    • You may have configured Spring Boot to not automatically serve whatever is inside of classpath:static/ or classpath:public/.