You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无法从数据库加载图片到Thymeleaf页面的问题求助

Spring Boot + Thymeleaf 图片压缩存储后无法显示排查

问题概述

Spring Boot项目中使用Thymeleaf开发,图片经压缩后存入数据库,读取时先解压再转换为Base64格式,但Thymeleaf页面无法正常显示图片。


相关代码与逻辑

1. Thymeleaf页面(editprofile.html)

<form th:action="@{/updateProfile}" method="post" class="signin-form" th:object="${editprofile}" enctype="multipart/form-data">
    <div class="title-main text-center mx-auto mb-md-5 mb-4" style="max-width:500px;">
        <h5 class="footer-title-29">Personal Details</h5>
        <div class="col-sm-6 profile_Pic">
            <img id="thumbnail" th:src="*{'data:image/jpg;base64,'+profile_pic}" width="200" />
        </div>
    </div>
    <br><br>
    <div class="input-grids">
        <label class="control-label" for="name">Name:</label>
        <input type="text" th:field="*{name}" id="name" class="contact-input" />
        <div class="row">
            <div class="col-sm-6">
                <label class="control-label" for="mobileNumber">Mobile Number:</label>
                <input type="text" th:field="*{mobileNumber}" id="mobileNumber" class="contact-input" />
            </div>
            <div class="col-sm-6">
                <label class="control-label" for="email">Email:</label>
                <input type="text" th:field="*{email}" id="email" class="contact-input" />
            </div>
        </div>
    </div>
    <div class="title-main text-center mx-auto mb-md-5 mb-4" style="max-width:500px;">
        <h5 class="footer-title-29">Address Details</h5>
    </div>
    <div class="input-grids">
        <label class="control-label" for="address1">Address Line 1:</label>
        <input type="text" th:field="*{address1}" id="address1" class="contact-input" />
        <label for="address2">Address Line 2:</label>
        <input type="text" th:field="*{address2}" id="address2" class="contact-input" />
        <label class="control-label" for="city">City:</label>
        <input type="text" th:field="*{city}" id="city" class="contact-input" />
        <div class="row">
            <div class="col-sm-6">
                <label class="control-label" for="state">State:</label>
                <input type="text" th:field="*{state}" id="state" class="contact-input" />
            </div>
            <div class="col-sm-6">
                <label class="control-label" for="zipCode">Zip Code:</label>
                <input type="text" th:field="*{zipCode}" id="zipCode" class="contact-input" />
            </div>
            <div class="col-sm-6">
                <label class="control-label" for="fileImage">Upload image</label>
                <input type="file" name="fileImage" accept="image/*" id="fileImage" />
            </div>
        </div>
    </div>
    <div class="col-md-8 login-center text-start">
        <button class="btn btn-style btn-style-3 text-left">Update</button>
        <a th:href="@{/dashboard}" class="new-user text-right">
            <button class="btn btn-style btn-style-3 text-left" th:formaction="@{/displayProfile}">BACK</button></a>
    </div>
</form>

2. 图片压缩工具类(ImageUtil.java)

public static byte[] compressImage(byte[] data) {
    Deflater deflater = new Deflater();
    deflater.setLevel(Deflater.BEST_COMPRESSION);
    deflater.setInput(data);
    deflater.finish();

    ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
    byte[] tmp = new byte[1024];

    while (!deflater.finished()) {
        int size = deflater.deflate(tmp);
        outputStream.write(tmp, 0, size);
    }

    try {
        outputStream.close();
    } catch (Exception e) {
    }

    return outputStream.toByteArray();
}

public static byte[] decompressImage(byte[] data) {
    Inflater inflater = new Inflater();
    inflater.setInput(data);
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
    byte[] tmp = new byte[1024];

    try {
        while (!inflater.finished()) {
            int count = inflater.inflate(tmp);
            outputStream.write(tmp, 0, count);
        }
        outputStream.close();
    } catch (Exception exception) {
    }

    return outputStream.toByteArray();
}

3. 图片上传逻辑

person.getAddress().setProfile_pic(ImageUtil.compressImage(file.getBytes()));
personRepository.save(person);

4. 图片读取逻辑

图片已成功存入数据库,读取时执行以下逻辑仍无法显示:

byte[] decomImage = ImageUtil.decompressImage(person.getAddress().getProfile_pic());
byte[] encodeBase64 = Base64.getEncoder().encode(decomImage);
String base64Encoded = new String(encodeBase64, "UTF-8");

将base64Encoded赋值给pojoprofile的encoded_image属性。

5. 控制器代码

编辑页面控制器

@RequestMapping(value = "/EditProfile")
public ModelAndView EditProfile(Model model, HttpSession httpSession){
    PersonPojo person=(PersonPojo) httpSession.getAttribute("loggedInPerson");
    Profile pojoprofile1=new Profile();
    pojoprofile1.setName(person.getName());
    pojoprofile1.setEmail(person.getEmail());
    pojoprofile1.setMobileNumber(person.getMobileNumber());
    if(person.getAddress()!=null&&person.getAddress().getAddressId()>0){
        pojoprofile1.setAddress1(person.getAddress().getAddress1());
        pojoprofile1.setAddress2(person.getAddress().getAddress2());
        pojoprofile1.setCity(person.getAddress().getCity());
        pojoprofile1.setState(person.getAddress().getState());
        pojoprofile1.setZipCode(person.getAddress().getZip_code());
        pojoprofile1.setProfile_pic(Base64.getEncoder().encode(person.getAddress().getProfile_pic()));

//            pojoprofile1.setProfile_pic(person.getAddress().getProfile_pic());
    }
    ModelAndView editprofile=new ModelAndView("EditProfile.html");
    editprofile.addObject("editprofile",pojoprofile1);
    return editprofile;
}

更新资料控制器

@RequestMapping(value = "/updateProfile")
public String updateProfile(@RequestParam("fileImage") MultipartFile file, @Valid @ModelAttribute("editprofile") Profile profile, Errors errors,
                            HttpSession httpSession) throws IOException {
    if(errors.hasErrors()){
        return "EditProfile.html";
    }
    PersonPojo person=(PersonPojo) httpSession.getAttribute("loggedInPerson");
    person.setName(profile.getName());
    person.setEmail(profile.getEmail());
    person.setMobileNumber(profile.getMobileNumber());
    if(person.getAddress() == null || !(person.getAddress().getAddressId()>0)){
        person.setAddress(new AddressPoJO());
    }
    person.getAddress().setAddress1(profile.getAddress1());
    person.getAddress().setAddress2(profile.getAddress2());
    person.getAddress().setCity(profile.getCity());
    person.getAddress().setState(profile.getState());
    person.getAddress().setZip_code(profile.getZipCode());
    person.getAddress().setProfile_pic(ImageUtil.compressImage(file.getBytes()));
    personRepository.save(person);
    httpSession.setAttribute("loggedInPerson", person);
    return "redirect:/dashboard";
}

资料展示控制器

@RequestMapping("/displayProfile")
public ModelAndView Myprofile(HttpSession httpSession) throws UnsupportedEncodingException {
    Profile pojoprofile=new Profile();
    PersonPojo person=(PersonPojo) httpSession.getAttribute("loggedInPerson");
    pojoprofile.setName(person.getName());
    pojoprofile.setEmail(person.getEmail());
    pojoprofile.setMobileNumber(person.getMobileNumber());
    if(person.getAddress()!=null&&person.getAddress().getAddressId()>0){
        log.info("Inside the if"+new MyProfile().getClass().toString());
        pojoprofile.setAddress1(person.getAddress().getAddress1());
        pojoprofile.setAddress2(person.getAddress().getAddress2());
        pojoprofile.setCity(person.getAddress().getCity());
        pojoprofile.setState(person.getAddress().getState());
        pojoprofile.setZipCode(person.getAddress().getZip_code());
        byte[] encodeBase64 = Base64.getEncoder().encode(ImageUtil.decompressImage(person.getAddress().getProfile_pic()));
        pojoprofile.setProfile_pic(encodeBase64);
    }else {
        pojoprofile.setAddress1(" Not available ");
        pojoprofile.setAddress2(" Not Available");
        pojoprofile.setZipCode(" Not Available");
        pojoprofile.setState(" Not Available");
        pojoprofile.setCity(" Not Available");
    }

    return new ModelAndView("profile.html").addObject("profile",pojoprofile);
}

排查与修复方案

1. 核心问题:Base64数据类型不匹配

当前控制器中,部分逻辑将byte[]类型的Base64数据赋值给Profile的profile_pic字段,而Thymeleaf拼接字符串时,会调用byte[]的toString()方法,生成[B@xxxxxxx这类无效的内存地址字符串,而非正确的Base64编码。

修复步骤:

  • 将Profile类的profile_pic字段类型改为String;
  • 在控制器中直接生成Base64字符串赋值:
    // 编辑页面控制器修正
    byte[] decompressed = ImageUtil.decompressImage(person.getAddress().getProfile_pic());
    String base64Str = Base64.getEncoder().encodeToString(decompressed);
    pojoprofile1.setProfile_pic(base64Str);
    
    // 展示控制器修正
    String base64Str = Base64.getEncoder().encodeToString(ImageUtil.decompressImage(person.getAddress().getProfile_pic()));
    pojoprofile.setProfile_pic(base64Str);
    

2. 压缩/解压异常排查

ImageUtil中的异常被静默捕获,无法得知是否解压失败。修改工具类添加异常日志:

public static byte[] decompressImage(byte[] data) {
    Inflater inflater = new Inflater();
    inflater.setInput(data);
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
    byte[] tmp = new byte[1024];

    try {
        while (!inflater.finished()) {
            int count = inflater.inflate(tmp);
            outputStream.write(tmp, 0, count);
        }
        outputStream.close();
    } catch (Exception exception) {
        exception.printStackTrace();
        throw new RuntimeException("图片解压失败", exception);
    }

    return outputStream.toByteArray();
}

3. 图片类型兼容性优化

页面中固定使用image/jpg,若上传PNG等其他格式图片会导致显示异常,可动态获取图片MIME类型,或改为image/*兼容更多类型:

<img id="thumbnail" th:src="*{'data:image/*;base64,'+profile_pic}" width="200" />

内容的提问来源于stack exchange,提问作者Nikhil Mathew

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 23:10:22