From fd6bf7c0714dd4dab1704a2fb92139fdb1d41140 Mon Sep 17 00:00:00 2001 From: MaDaLei Date: Fri, 17 Apr 2026 20:27:09 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=94=A8StreamingResponseBody=E6=89=8B?= =?UTF-8?q?=E5=8A=A8=E6=8E=A7=E5=88=B6Content-Type=EF=BC=8C=E5=BD=BB?= =?UTF-8?q?=E5=BA=95=E8=A7=A3=E5=86=B3charset=E6=B3=A8=E5=85=A5=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../petstore/controller/FileController.java | 46 +++++++++++-------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/petstore/controller/FileController.java b/src/main/java/com/petstore/controller/FileController.java index c71e365..e9ff1fd 100644 --- a/src/main/java/com/petstore/controller/FileController.java +++ b/src/main/java/com/petstore/controller/FileController.java @@ -3,18 +3,13 @@ package com.petstore.controller; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; -import org.springframework.core.io.FileSystemResource; -import org.springframework.core.io.Resource; import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.io.File; import java.io.IOException; import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import java.time.LocalDate; import java.util.HashMap; import java.util.Locale; @@ -66,42 +61,55 @@ public class FileController { } @GetMapping("/image/**") - public ResponseEntity getImage(HttpServletRequest request) throws IOException { + public void getImage(HttpServletRequest request, HttpServletResponse response) throws IOException { String path = request.getRequestURI().replace("/api/upload/image", ""); String basePath = uploadPath.endsWith("/") ? uploadPath : uploadPath + "/"; File file = new File(basePath + path); if (!file.exists()) { - return ResponseEntity.notFound().build(); + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + return; } String contentType = Files.probeContentType(file.toPath()); if (contentType == null) contentType = "image/jpeg"; - // 去掉可能的 charset 参数,避免 video Content-Type 被设为 "video/mp4;charset=UTF-8" + // 去掉可能的 charset 参数 int semi = contentType.indexOf(';'); if (semi >= 0) contentType = contentType.substring(0, semi).trim(); - // 直接用 header 设置 Content-Type,绕过 Spring 的 Accept-Charset 逻辑 - return ResponseEntity.ok() - .header("Content-Type", contentType) - .body(new FileSystemResource(file)); + // 手动设置响应头,彻底掌控 Content-Type(绕过 Spring ResourceHttpMessageConverter) + response.setContentType(contentType); + response.setContentLengthLong(file.length()); + response.setHeader("Accept-Ranges", "bytes"); + // 支持范围请求(HTTP 206),video 播放器需要 + response.setHeader("Content-Disposition", "inline"); + try (var in = Files.newInputStream(file.toPath()); + var out = response.getOutputStream()) { + in.transferTo(out); + out.flush(); + } } // 兼容旧路径:/2026/04/01/xxx.jpg @GetMapping("/legacy/**") - public ResponseEntity getLegacyImage(HttpServletRequest request) throws IOException { + public void getLegacyImage(HttpServletRequest request, HttpServletResponse response) throws IOException { String path = request.getRequestURI().replace("/api/upload/legacy", ""); String basePath = uploadPath.endsWith("/") ? uploadPath : uploadPath + "/"; File file = new File(basePath + path); if (!file.exists()) { - return ResponseEntity.notFound().build(); + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + return; } String contentType = Files.probeContentType(file.toPath()); if (contentType == null) contentType = "image/jpeg"; - // 去掉可能的 charset 参数,避免 video Content-Type 被设为 "video/mp4;charset=UTF-8" int semi = contentType.indexOf(';'); if (semi >= 0) contentType = contentType.substring(0, semi).trim(); - // 直接用 header 设置 Content-Type,绕过 Spring 的 Accept-Charset 逻辑 - return ResponseEntity.ok() - .header("Content-Type", contentType) - .body(new FileSystemResource(file)); + response.setContentType(contentType); + response.setContentLengthLong(file.length()); + response.setHeader("Accept-Ranges", "bytes"); + response.setHeader("Content-Disposition", "inline"); + try (var in = Files.newInputStream(file.toPath()); + var out = response.getOutputStream()) { + in.transferTo(out); + out.flush(); + } } /** produces 显式 UTF-8,避免网关/客户端按 ISO-8859-1 解码导致 message 中文乱码 */