javabase64thymeleafbytea

Is there a way to display image encoded into a string longer than 100 000 characters? (Java, Spring, Thymeleaf)


So, i have a form that allows user to upload an image, which will be encoded with IOUtils.toByteArray and persisted to a database as a bytea. In a controller method i get this byte array and encode it to string:

@GetMapping("/{user_id}")
public String view(@PathVariable("user_id") Long user_id, Model model) {
    User user = userService.getById(user_id);
    model.addAttribute("user", user);
    byte[] profilePictureBytes = user.getProfilePicture();

    if (profilePictureBytes != null) {
        String encodedImage = Base64.getEncoder().encodeToString(profilePictureBytes);
        model.addAttribute("encodedImage", encodedImage);
    }
    return "user-page";
}

On a user-page html file i try to decode it like this:

<img th:attr="src=${'data:image/jpeg;base64,' + encodedImage}" alt="Profile Picture">

This solution works for smaller images but throws an exception when the encoded image exceeds 100 000 characters:

org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "'data:image/jpeg;base64,' + encodedImage" (template: "user-page" - line 27, col 6)

org.springframework.expression.spel.SpelEvaluationException: EL1078E: Concatenated string is too long, exceeding the threshold of '100 000' characters

Is there a way to circumvent this limit or should i change the whole logic of the program instead? Thank you.


Solution

  • This one is tricky! TY, @Andrey(, again;) for exactly pointing the "issue". I'm not sure, whether it is worth to open a spring issue/request/enhancement for this.

    But:

    Resulting template:

    <img src="data:image/jpeg;base64," th:attrappend="src=${encodedImage}" alt="Profile Picture">
    

    (Test) Controller used:

    @Controller
    class DemoController {
    
        final String encodedImage;
    
        public DemoController(
                @Value("classpath:/bigImage.jpg") /* source: https://github.com/samdutton/simpl/blob/main/bigimage/bigImage.jpg */
                Resource bigPic) throws IOException {
            this.encodedImage = Base64.getEncoder().encodeToString(bigPic.getContentAsByteArray());
        }
    
        @ModelAttribute("encodedImage")
        public String encodedImage() throws IOException {
            return encodedImage;
        }
    
        @GetMapping("/test")
        public String test() {
            return "test";
        }
    }
    

    Generally

    "These type of things" belong "into the caches of load balancers"! (..and not into the "memory" (byte[], Base64...) of your "back ends" :)

    Advanced

    It'd be nice to "attach" these (kind of) things (to html/http response) similar to https://www.thymeleaf.org/doc/articles/springmail.html, like:

    Another (Simple) Workaround

    @ModelAttribute("encodedImageAttr")
    public String encodedImageAttr() throws IOException {
        return String.format("data:image/jpeg;base64,%s", encodedImage);;
    }
    

    ...serve the complete attribute value instead of the "base64 part".

    Template then looks like:

    <img src="" th:attr="src=${encodedImageAttr}" alt="Profile Picture">
    

    (I also tried with template SpEL th:attr="src=${#T(java.lang.String).format('data:image/jpeg;base64,%s', encodedImageAttr)}", but failed due to:

    ...
    Caused by: org.attoparser.ParseException: Instantiation of new objects and access to static classes or parameters is forbidden in this context (template: "test" - line 6, col 17)
        at org.attoparser.MarkupParser.parseDocument(MarkupParser.java:393)
        at org.attoparser.MarkupParser.parse(MarkupParser.java:257)
        at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parse(AbstractMarkupTemplateParser.java:230)
        ... 52 more
    Caused by: org.thymeleaf.exceptions.TemplateProcessingException: Instantiation of new objects and access to static classes or parameters is forbidden in this context (template: "test" - line 6, col 17)
        at org.thymeleaf.spring6.expression.SPELVariableExpressionEvaluator.obtainComputedSpelExpression(SPELVariableExpressionEvaluator.java:309)
        at org.thymeleaf.spring6.expression.SPELVariableExpressionEvaluator.evaluate(SPELVariableExpressionEvaluator.java:182)
        at org.thymeleaf.standard.expression.VariableExpression.executeVariableExpression(VariableExpression.java:166)
        at org.thymeleaf.standard.expression.SimpleExpression.executeSimple(SimpleExpression.java:66)
        at org.thymeleaf.standard.expression.Expression.execute(Expression.java:109)
    ...
    

    )