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

SpringBoot+Thymeleaf点击图片显示乱码而非JPEG的问题求助

图片点击显示乱码问题排查与解决

我通过表单上传图片并存储到数据库,在本地localhost运行时,图片能在表格中正常显示,但点击图片链接后,页面显示乱码而非JPEG格式的图片。技术栈为SpringBoot + Thymeleaf。


HTML相关代码

<!DOCTYPE html>
<html lang="es"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="~{master.html}">
<head>
    <meta charset="UTF-8">
    <title>Administrar películas | Trailers </title>
</head>
<body>
<div layout:fragment="content">
    <h2> Peliculas</h2>

    <div class="text-end">
        <a href="/admin/peliculas/nuevo" class="btn btn-secondary mb-3">
            Nueva Película
        </a>
    </div>

    <table class="table table-hover">
        <thead>
        <tr>
            <th></th>
            <th>Pelicula</th>
            <th>Fecha de estreno</th>
            <th></th>
        </tr>
        </thead>
        <tbody>
        <tr th:each="pelicula : ${peliculas}" th:object="${pelicula}">
        <td style="width: 150px;">
            <a th:href="@{/assets/{filename}(filename=*{rutaPortada})}" class="img-thumbnail w-100" data-fancybox>
                <img th:src="@{/assets/{filename}(filename=*{rutaPortada})}" alt="" class="img-thumbnail w-100" >
            </a>
            </td>
            <td>
                <div th:text="*{titulo}" class="fs-5 fw-bold"></div>
                <p th:text="*{sinopsis}"></p>
                <div>
                    <span class="badge rounded-pill bg-light text-dark me-1"
                          th:each="genero : *{generos}"
                             th:text="${genero.titulo}"></span>
                </div>
            </td>
            <td th:text="*{#temporals.format(fechaEstreno, 'dd/MM/yyyy')}"></td>
            <td class="text-end">
                <a th:href="@{/admin/peliculas/{id}/editar(id=*{id})}">Editar</a>
            </td>
        </tr>
        </tbody>
    </table>
</div>
</body>
</html>

Controller相关代码

package com.cinetrailer.trailer.controller;

import com.cinetrailer.trailer.model.Genero;
import com.cinetrailer.trailer.model.Pelicula;
import com.cinetrailer.trailer.repo.GeneroRepository;
import com.cinetrailer.trailer.repo.PeliculaRepository;
import com.cinetrailer.trailer.service.FileSystemStorageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import java.util.List;
import java.util.Optional;

@Controller
@RequestMapping("/admin")
public class AdminController {

    @Autowired
    private PeliculaRepository peliculaRepository;
    @Autowired
    private GeneroRepository generoRepository;
    @Autowired
    private FileSystemStorageService fileSystemStorageService;

    @GetMapping("")
    ModelAndView index(@PageableDefault(sort = "titulo", size = 5) Pageable pageable) {
        Page<Pelicula> peliculas = peliculaRepository.findAll(pageable);
        return new ModelAndView("admin/index").addObject("peliculas", peliculas);
    }

    @GetMapping("/peliculas/nuevo")
    ModelAndView nuevaPelicula() {
        List<Genero> generos = generoRepository.findAll(Sort.by("titulo"));

        return new ModelAndView("admin/nueva-pelicula").addObject("pelicula", new Pelicula()).addObject("generos", generos);
    }

    @PostMapping("/peliculas/nuevo")
    ModelAndView crearPelicula(@Validated Pelicula pelicula, BindingResult bindingResult) {

        if (bindingResult.hasErrors() || pelicula.getPortada().isEmpty()) {
            if (pelicula.getPortada().isEmpty()) {
                bindingResult.rejectValue("portada", "MultipartNotEmpty");
            }
            List<Genero> generos = generoRepository.findAll(Sort.by("titulo"));
            return new ModelAndView("admin/nueva-pelicula").addObject("pelicula", pelicula).addObject("generos", generos);
        }
        String rutaPortada = fileSystemStorageService.storage(pelicula.getPortada());
        pelicula.setRutaPortada(rutaPortada);

        peliculaRepository.save(pelicula);

        return new ModelAndView("redirect:/admin");
    }

    @GetMapping("/peliculas/{id}/editar")
    ModelAndView editarPelicula(@PathVariable Integer id) {
        Optional<Pelicula> optionalPelicula = peliculaRepository.findById(id);
        if (optionalPelicula.isPresent()) {
            Pelicula pelicula = optionalPelicula.get();
            List<Genero> generos = generoRepository.findAll(Sort.by("titulo"));

            return new ModelAndView("admin/editar-pelicula")
                    .addObject("pelicula", pelicula).addObject("generos", generos);
        }
        else {
            return new ModelAndView("error");
        }
    }
}

问题原因与解决方案

问题根源

点击图片时的/assets/{filename}请求没有被正确处理,服务器默认以文本格式返回数据,而非二进制图片流,导致浏览器将二进制数据解析为乱码。

解决步骤

  1. 添加图片资源处理Controller方法
    在现有Controller中新增以下方法,专门处理图片请求:
import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@GetMapping("/assets/{filename:.+}")
public ResponseEntity<Resource> serveImage(@PathVariable String filename) {
    Resource imageFile = fileSystemStorageService.loadAsResource(filename);
    
    // 动态获取文件MIME类型,避免硬编码
    String contentType = "image/jpeg";
    try {
        contentType = Files.probeContentType(imageFile.getFile().toPath());
    } catch (IOException e) {
        // 异常时 fallback 为jpeg
    }

    return ResponseEntity.ok()
            .contentType(MediaType.parseMediaType(contentType))
            .header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"" + imageFile.getFilename() + "\"")
            .body(imageFile);
}
  1. 完善FileSystemStorageService的文件加载方法
    确保你的文件存储服务实现了加载文件为Resource的方法:
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import java.nio.file.Path;
import java.nio.file.Paths;

public class FileSystemStorageService {
    private final Path storagePath;

    // 构造函数注入存储路径,可从application.properties读取
    public FileSystemStorageService(String uploadDir) {
        this.storagePath = Paths.get(uploadDir).toAbsolutePath().normalize();
    }

    public Resource loadAsResource(String filename) {
        try {
            Path file = storagePath.resolve(filename).normalize();
            Resource resource = new UrlResource(file.toUri());
            if (resource.exists() && resource.isReadable()) {
                return resource;
            }
            throw new RuntimeException("无法加载图片文件: " + filename);
        } catch (Exception e) {
            throw new RuntimeException("加载图片失败", e);
        }
    }

    // 你的storage上传方法...
}
  1. 配置存储路径
    在application.properties中添加存储路径配置:
file.upload-dir=./uploads

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 18:42:08