How to display byte array from a model in Thymeleaf
Asked Answered
A

3

8

I want to display all my products info, but I have problem with showing a product image. I get my products from DB and then I add them to Model, but don't know why only image don't display. In HTML it looks like this:

<div class="row">
    <div class="col-sm-3 my-2 d-flex align-content-stretch flex-wrap" th:each="product : ${products}">
        <div class="card">
            <img class="card-img-top" th:src="${product.image}">
            <div class="card-body">
                <h5 class="card-title" th:text="${product.name}">Product name</h5>
                <p class="card-text" th:text="${product.description}">Product summary</p>
                <p class="card-text" th:text="${product.cost}">Product summary</p>
            </div>
        </div>
    </div>
</div>

In controller I add all the products like this:

@GetMapping("/")
public String getHomePage(Model model) {
    model.addAttribute("products", productRepository.findAll());
    return "home";
}

And the product model is as shown:

@Entity
@Getter
public class Product extends BaseEntity {

    private String name;

    private String description;

    @OneToOne
    private Category category;

    private double cost;

    @Lob
    private byte[] image;

    public Product() {
        super();
    }

    public Product(String name, String description, Category category, double cost, byte[] image) {
        this();
        this.name = name;
        this.description = description;
        this.category = category;
        this.cost = cost;
        this.image = image;
    }
}

My problem is that I want to display multiple images at one time. BTW, I know that the findAll method is not a good choose, but It is only for testing proposes. Later I want to implement pagination, but first how to display, a byte array image?

Ablation answered 12/1, 2018 at 23:24 Comment(3)
I think you can convert byte array to base64 string then display it.image = Base64.getEncoder().encode(image); and in view < img th:src="*{'data:image/png;base64,'+image}" alt="" />Agnusago
In the Base64 class there in no method encode which return String, but there is encodeToString which works perfect. Thank You for help.Ablation
Well I tried to do the same, but was unsuccessful apparently Thymeleaf was not accepting byte[] and so I had to Base64.getEncoder().encodeToString(byte[]) before setting it as Context VariableUnquote
P
11

I'm answering this old question, in the hope of helping someone with the same need.

In order to show image using bytes, you have to create a controller action with the role of showing the image:

@GetMapping("/product/image/{id}")
public void showProductImage(@PathVariable String id
                               HttpServletResponse response) throws IOException {
response.setContentType("image/jpeg"); // Or whatever format you wanna use

Product product = productRepository.findById(id);

InputStream is = new ByteArrayInputStream(product.getImage());
IOUtils.copy(is, response.getOutputStream());
}

Thus, you can simply show your image with:

<div class="row">
    <div class="col-sm-3 my-2 d-flex align-content-stretch flex-wrap" th:each="product : ${products}">
        <div class="card">
            <img class="card-img-top" th:src="@{'product/image/' + @{product.image}}">
            <div class="card-body">
                <h5 class="card-title" th:text="${product.name}">Product name</h5>
                <p class="card-text" th:text="${product.description}">Product summary</p>
                <p class="card-text" th:text="${product.cost}">Product summary</p>
            </div>
        </div>
    </div> 

PS: you better use the wrapper Byte in spite of byte for your image attribute. This will let you manage cases of no image (null)

Edit: The last line of showProductImage method is copying InputStream to OutputStream (check IOUtils doc for more details)

Peake answered 18/4, 2019 at 9:50 Comment(2)
You can make this answer better by defining what is IOUtils and how it affects the requestEveryone
@samuelowino just added it thanks for your suggestion Notice: I was actually missing to call the right method (copy)Peake
T
3

Using Spring Boot and having the same issue, I was not convinced by using HttpServletResponse to get the image in a separated request since we have already retrieved the image byte array included in model attributes, I was wondering like: How can we load the image from the byte array itself ?

Found the solution by converting the image byte array to base64 string and pass it to JavaScript to fill the image preview element:

In Java

Add an extra String attribute representing the base64 encoding value of the image:

private String imageBase64;

public String getImageBase64() {
    return imageBase64;
}

public void setImageBase64(String imageBase64) {
    this.imageBase64 = imageBase64;
}

After getting object from database, add an extra step of base64 conversion from image byte array as follows:

import java.util.Base64;
import org.apache.commons.lang3.ArrayUtils;

if(ArrayUtils.isNotEmpty(product.getImage())) {
    String imageBase64 = Base64.getEncoder().encodeToString(product.getImage());
    productDTO.setImageBase64(imageBase64);
}

In JSP/HTML

Add an extra hidden field containing the base64 string of the image

<input type="hidden" id="imageBase64" value="${product.imageBase64}" readonly></input>
<img id="imagePreview" src="#" alt="Background image" />

In JavaScript

Set the image content by the base64 value from the hidden field

var imageBase64 = $('#imageBase64').val();
var imagePreview= document.getElementById('imagePreview');
if(imagePreview) {
    imagePreview.src = "data:image/png;base64," + imageBase64;
}

Besides, can also store the image as Base64 string and get rid of the custom conversion phase...

Truck answered 22/1, 2021 at 16:18 Comment(0)
I
0

this is what worked for me, convert image to Base64 and inject it to Thymeleaf using a map for example

        Map<Long, String> productBase64Images = new HashMap<>();
        for(Product product: products){               
            productBase64Images.put(product.getId(), Base64.getEncoder().encodeToString(product.getImage()));
        }
        model.addAttribute("images", productBase64Images);

display the image using : <img th:src="${'data:image/jpeg;charset=utf-8;base64,' + images.get(product.id)}" alt="">

Islaen answered 9/9, 2022 at 14:54 Comment(1)
Is this code storing the images temporairy? Will it have conflicts if multiple requests are done at once?Huonghupeh

© 2022 - 2024 — McMap. All rights reserved.