Issue
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.
Solution
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
Answered By - Leonardo Emmanuel de Azevedo
Answer Checked By - Marilyn (JavaFixing Volunteer)