From 6cbf23c06a3fa2af5f74938b1e0749f6a6dfaf15 Mon Sep 17 00:00:00 2001 From: MaDaLei Date: Fri, 17 Apr 2026 20:31:50 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20FileController=E6=81=A2=E5=A4=8D?= =?UTF-8?q?=E6=A0=87=E5=87=86=E5=AE=9E=E7=8E=B0=EF=BC=8C=E7=A1=AE=E4=BF=9D?= =?UTF-8?q?=E7=BC=96=E8=AF=91=E5=92=8C=E9=83=A8=E7=BD=B2=E6=AD=A3=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../petstore/controller/FileController.java | 62 ++++++++----------- 1 file changed, 26 insertions(+), 36 deletions(-) diff --git a/src/main/java/com/petstore/controller/FileController.java b/src/main/java/com/petstore/controller/FileController.java index e9ff1fd..c03a9bc 100644 --- a/src/main/java/com/petstore/controller/FileController.java +++ b/src/main/java/com/petstore/controller/FileController.java @@ -3,13 +3,18 @@ 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.HttpStatus; 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.Paths; import java.time.LocalDate; import java.util.HashMap; import java.util.Locale; @@ -23,14 +28,10 @@ import java.util.UUID; @CrossOrigin public class FileController { - private static final Set IMAGE_EXT = Set.of( - ".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".heic", ".heif", ".jpe" - ); - private static final Set VIDEO_EXT = Set.of( - ".mp4", ".mov", ".m4v", ".webm", ".avi", ".mkv", ".3gp" - ); + private final Set IMAGE_EXT = Set.of(".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".heic", ".heif"); + private final Set VIDEO_EXT = Set.of(".mp4", ".mov", ".avi", ".mkv", ".flv", ".wmv", ".webm"); - @Value("${upload.path:uploads/}") + @Value("${upload.path}") private String uploadPath; /** @@ -61,57 +62,45 @@ public class FileController { } @GetMapping("/image/**") - public void getImage(HttpServletRequest request, HttpServletResponse response) throws IOException { + public ResponseEntity getImage(HttpServletRequest request) 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()) { - response.setStatus(HttpServletResponse.SC_NOT_FOUND); - return; + return ResponseEntity.notFound().build(); } String contentType = Files.probeContentType(file.toPath()); if (contentType == null) contentType = "image/jpeg"; - // 去掉可能的 charset 参数 + // 去掉 charset 参数(Spring 的 Accept-Charset 会错误地给 binary 类型加上 charset) int semi = contentType.indexOf(';'); if (semi >= 0) contentType = contentType.substring(0, semi).trim(); - // 手动设置响应头,彻底掌控 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(); - } + MediaType mediaType = MediaType.parseMediaType(contentType); + return ResponseEntity.ok() + .contentType(mediaType) + .contentLength(file.length()) + .body(new FileSystemResource(file)); } - + // 兼容旧路径:/2026/04/01/xxx.jpg @GetMapping("/legacy/**") - public void getLegacyImage(HttpServletRequest request, HttpServletResponse response) throws IOException { + public ResponseEntity getLegacyImage(HttpServletRequest request) 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()) { - response.setStatus(HttpServletResponse.SC_NOT_FOUND); - return; + return ResponseEntity.notFound().build(); } String contentType = Files.probeContentType(file.toPath()); if (contentType == null) contentType = "image/jpeg"; int semi = contentType.indexOf(';'); if (semi >= 0) contentType = contentType.substring(0, semi).trim(); - 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(); - } + MediaType mediaType = MediaType.parseMediaType(contentType); + return ResponseEntity.ok() + .contentType(mediaType) + .contentLength(file.length()) + .body(new FileSystemResource(file)); } - + /** produces 显式 UTF-8,避免网关/客户端按 ISO-8859-1 解码导致 message 中文乱码 */ @PostMapping(value = "/image", produces = MediaType.APPLICATION_JSON_VALUE + ";charset=UTF-8") public Map uploadImage(@RequestParam("file") MultipartFile file) { @@ -155,6 +144,7 @@ public class FileController { } ext = looksVideo ? ".mp4" : ".jpg"; } + String filename = UUID.randomUUID().toString().replace("-", "") + ext; // 保存文件(流式写入,避免大视频一次性进内存)