2

I'm currently trying to teach myself Spring Boot by writing a simple tabletop simulator for the popular card game Magic the Gathering. So what I want to do first is retrieve card images from the Scryfall API and display them in Thymeleaf. I know how to do this for static images, but I can't seem to find a way to display images retrieved dynamically. My current workaround is to provide Thymeleaf with the Scryfall URI, but what I really want to do is to display a BufferedImage in Thymeleaf. So here's my current code for the controller.

package mtg;

import java.util.Map;

import org.springframework.boot.json.BasicJsonParser;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

@Controller
@RequestMapping("/sample")
public class SampleCardController {

@ModelAttribute
public void addCardToModel(Model model) {
    RestTemplate rest = new RestTemplate();
    String jsonString = rest.getForObject(
            "https://api.scryfall.com/cards/random", String.class);
    BasicJsonParser parser = new BasicJsonParser();
    Map<String, Object> map = parser.parseMap(jsonString);
    String name = (String) map.get("name");
    String uri = (String) map.get("uri");
    model.addAttribute("cardName", name);
    model.addAttribute("imageURI", uri + "?format=image");
}

@GetMapping
public String showSampleCard() {
    return "sample";
}
}

And this is the Thymeleaf template sample.html:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
  <head>
    <title>Sample</title>
  </head>
  <body>
    <h1>Here's a sample card!</h1>
    <h3 th:text="${cardName}"></h3>
    <img th:src="${imageURI}"/>
  </body>
</html>

What I really want to be doing in the controller, is something like this:

package mtg;

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;

import javax.imageio.ImageIO;

import org.springframework.boot.json.BasicJsonParser;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

@Controller
@RequestMapping("/sample")
public class SampleCardController2 {

    @ModelAttribute
    public void addCardToModel(Model model) {
        RestTemplate rest = new RestTemplate();
        String jsonString = rest.getForObject(
                "https://api.scryfall.com/cards/random", String.class);
        BasicJsonParser parser = new BasicJsonParser();
        Map<String, Object> map = parser.parseMap(jsonString);
        String name = (String) map.get("name");
        String imageURI = (String) map.get("uri");
        BufferedImage image = null;
        try {
            image = ImageIO.read(new URL(imageURI + "?format=image"));
        } catch (MalformedURLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        model.addAttribute("cardName", name);
        model.addAttribute("image", image);
    }

    @GetMapping
    public String showSampleCard() {
        return "sample";
    }

}

But I don't know how to get Thymeleaf to display the image. It seems that for the img tag`you can only provide a th:src attribute which needs an URL. Is there something similar to th:text="${cardName}" for images where you can use the name of the model attribute?

EDIT: See @Lee Greiner 's comment below for how to fix the template.

Markus
  • 47
  • 6

1 Answers1

2

You can insert more than just an URL in the img tag. In this case, you can insert the base64 representation of the image you are loading by doing this:

<div>
  <p>Taken from wikpedia</p>
  <img src="data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAAUA
    AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
        9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red dot" />
</div>

And from your code, get the base64 from the BefferedImage object like this:

public static String imgToBase64String(final RenderedImage img, final String formatName)
{
  final ByteArrayOutputStream os = new ByteArrayOutputStream();

  try
  {
    ImageIO.write(img, formatName, os);
    return Base64.getEncoder().encodeToString(os.toByteArray());
  }
  catch (final IOException ioe)
  {
    throw new UncheckedIOException(ioe);
  }
}

And pass it to the view.

Links IMG tag base64 src

BufferedImage to base64

  • Ok, maybe I'm missing something, but in the html you provided, the `src` tag seems to contain the image as a kind of string literal. How do I get the return value from the `imgToBase64String` method into the src tag? – Markus Sep 22 '22 at 11:26
  • As you are doing right now, in the `addCardToModel()` method, when you are doing `model.addAttribute("imageURI", uri + "?format=image");`, instead of sending the uri, you send the value returned by `imgToBase64String` – Leonardo Emmanuel de Azevedo Sep 22 '22 at 12:10
  • Ok, so if I put the return value in a variable, e.g. `String imageString`, I then do `model.addAttribute("imageString", imageString);` And then I write `` in the template? – Markus Sep 22 '22 at 13:09
  • Exactly like that, it should work. – Leonardo Emmanuel de Azevedo Sep 22 '22 at 13:23
  • I tried it, but I got `org.thymeleaf.TemplateEngine : [THYMELEAF][http-nio-8080-exec-1] Exception processing template "sample": An error happened during template parsing (template: "class path resource [templates/sample.html]")`. This is the line in the template: ``. – Markus Sep 22 '22 at 13:45
  • [This link](https://stackoverflow.com/questions/63683474/how-to-put-variable-to-img-src-path-in-spring-with-thymeleaf) tells you how to fix it – Leonardo Emmanuel de Azevedo Sep 22 '22 at 18:06
  • 1
    I think you are looking for `````` – Lee Greiner Sep 22 '22 at 20:47
  • @Lee Greiner Thanks, that worked! I will accept @ Leonardo Emmanuel de Azevedo 's answer and edit my question to point to your comment. – Markus Sep 23 '22 at 09:46