fix: FileController恢复标准实现,确保编译和部署正常
This commit is contained in:
parent
fd6bf7c071
commit
6cbf23c06a
@ -3,13 +3,18 @@ package com.petstore.controller;
|
|||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
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.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@ -23,14 +28,10 @@ import java.util.UUID;
|
|||||||
@CrossOrigin
|
@CrossOrigin
|
||||||
public class FileController {
|
public class FileController {
|
||||||
|
|
||||||
private static final Set<String> IMAGE_EXT = Set.of(
|
private final Set<String> IMAGE_EXT = Set.of(".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".heic", ".heif");
|
||||||
".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".heic", ".heif", ".jpe"
|
private final Set<String> VIDEO_EXT = Set.of(".mp4", ".mov", ".avi", ".mkv", ".flv", ".wmv", ".webm");
|
||||||
);
|
|
||||||
private static final Set<String> VIDEO_EXT = Set.of(
|
|
||||||
".mp4", ".mov", ".m4v", ".webm", ".avi", ".mkv", ".3gp"
|
|
||||||
);
|
|
||||||
|
|
||||||
@Value("${upload.path:uploads/}")
|
@Value("${upload.path}")
|
||||||
private String uploadPath;
|
private String uploadPath;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -61,55 +62,43 @@ public class FileController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/image/**")
|
@GetMapping("/image/**")
|
||||||
public void getImage(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
public ResponseEntity<Resource> getImage(HttpServletRequest request) throws IOException {
|
||||||
String path = request.getRequestURI().replace("/api/upload/image", "");
|
String path = request.getRequestURI().replace("/api/upload/image", "");
|
||||||
String basePath = uploadPath.endsWith("/") ? uploadPath : uploadPath + "/";
|
String basePath = uploadPath.endsWith("/") ? uploadPath : uploadPath + "/";
|
||||||
File file = new File(basePath + path);
|
File file = new File(basePath + path);
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
return ResponseEntity.notFound().build();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
String contentType = Files.probeContentType(file.toPath());
|
String contentType = Files.probeContentType(file.toPath());
|
||||||
if (contentType == null) contentType = "image/jpeg";
|
if (contentType == null) contentType = "image/jpeg";
|
||||||
// 去掉可能的 charset 参数
|
// 去掉 charset 参数(Spring 的 Accept-Charset 会错误地给 binary 类型加上 charset)
|
||||||
int semi = contentType.indexOf(';');
|
int semi = contentType.indexOf(';');
|
||||||
if (semi >= 0) contentType = contentType.substring(0, semi).trim();
|
if (semi >= 0) contentType = contentType.substring(0, semi).trim();
|
||||||
// 手动设置响应头,彻底掌控 Content-Type(绕过 Spring ResourceHttpMessageConverter)
|
MediaType mediaType = MediaType.parseMediaType(contentType);
|
||||||
response.setContentType(contentType);
|
return ResponseEntity.ok()
|
||||||
response.setContentLengthLong(file.length());
|
.contentType(mediaType)
|
||||||
response.setHeader("Accept-Ranges", "bytes");
|
.contentLength(file.length())
|
||||||
// 支持范围请求(HTTP 206),video 播放器需要
|
.body(new FileSystemResource(file));
|
||||||
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
|
// 兼容旧路径:/2026/04/01/xxx.jpg
|
||||||
@GetMapping("/legacy/**")
|
@GetMapping("/legacy/**")
|
||||||
public void getLegacyImage(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
public ResponseEntity<Resource> getLegacyImage(HttpServletRequest request) throws IOException {
|
||||||
String path = request.getRequestURI().replace("/api/upload/legacy", "");
|
String path = request.getRequestURI().replace("/api/upload/legacy", "");
|
||||||
String basePath = uploadPath.endsWith("/") ? uploadPath : uploadPath + "/";
|
String basePath = uploadPath.endsWith("/") ? uploadPath : uploadPath + "/";
|
||||||
File file = new File(basePath + path);
|
File file = new File(basePath + path);
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
return ResponseEntity.notFound().build();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
String contentType = Files.probeContentType(file.toPath());
|
String contentType = Files.probeContentType(file.toPath());
|
||||||
if (contentType == null) contentType = "image/jpeg";
|
if (contentType == null) contentType = "image/jpeg";
|
||||||
int semi = contentType.indexOf(';');
|
int semi = contentType.indexOf(';');
|
||||||
if (semi >= 0) contentType = contentType.substring(0, semi).trim();
|
if (semi >= 0) contentType = contentType.substring(0, semi).trim();
|
||||||
response.setContentType(contentType);
|
MediaType mediaType = MediaType.parseMediaType(contentType);
|
||||||
response.setContentLengthLong(file.length());
|
return ResponseEntity.ok()
|
||||||
response.setHeader("Accept-Ranges", "bytes");
|
.contentType(mediaType)
|
||||||
response.setHeader("Content-Disposition", "inline");
|
.contentLength(file.length())
|
||||||
try (var in = Files.newInputStream(file.toPath());
|
.body(new FileSystemResource(file));
|
||||||
var out = response.getOutputStream()) {
|
|
||||||
in.transferTo(out);
|
|
||||||
out.flush();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** produces 显式 UTF-8,避免网关/客户端按 ISO-8859-1 解码导致 message 中文乱码 */
|
/** produces 显式 UTF-8,避免网关/客户端按 ISO-8859-1 解码导致 message 中文乱码 */
|
||||||
@ -155,6 +144,7 @@ public class FileController {
|
|||||||
}
|
}
|
||||||
ext = looksVideo ? ".mp4" : ".jpg";
|
ext = looksVideo ? ".mp4" : ".jpg";
|
||||||
}
|
}
|
||||||
|
|
||||||
String filename = UUID.randomUUID().toString().replace("-", "") + ext;
|
String filename = UUID.randomUUID().toString().replace("-", "") + ext;
|
||||||
|
|
||||||
// 保存文件(流式写入,避免大视频一次性进内存)
|
// 保存文件(流式写入,避免大视频一次性进内存)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user