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}请求没有被正确处理,服务器默认以文本格式返回数据,而非二进制图片流,导致浏览器将二进制数据解析为乱码。
解决步骤
- 添加图片资源处理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); }
- 完善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上传方法... }
- 配置存储路径
在application.properties中添加存储路径配置:
file.upload-dir=./uploads
内容的提问来源于stack exchange,提问作者user22130334
相关产品推荐
相关产品推荐

