Skip to content

Commit 677439d

Browse files
author
yqz
committed
fix:解决头像无法展示问题
1 parent 154e069 commit 677439d

3 files changed

Lines changed: 48 additions & 11 deletions

File tree

OpenBlog-business/src/main/java/com/yqz/openblog/common/GlobalExceptionHandler.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import org.springframework.web.bind.MethodArgumentNotValidException;
66
import org.slf4j.Logger;
77
import org.slf4j.LoggerFactory;
8+
import java.io.IOException;
9+
810
import org.springframework.web.bind.annotation.ExceptionHandler;
911
import org.springframework.web.bind.annotation.RestControllerAdvice;
1012
import org.springframework.security.access.AccessDeniedException;
@@ -45,10 +47,21 @@ public ResponseEntity<ApiResponse<Object>> onAccessDenied(Exception ex) {
4547
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(ApiResponse.fail(4030, "无权限"));
4648
}
4749

50+
/**
51+
* IO 异常(MinIO 读写、文件读写等)直接返回原始错误信息,便于排查。
52+
*/
53+
@ExceptionHandler(IOException.class)
54+
public ResponseEntity<ApiResponse<Object>> onIO(IOException ex) {
55+
log.error("IO exception", ex);
56+
String msg = ex.getMessage() != null ? ex.getMessage() : "IO异常";
57+
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ApiResponse.fail(5001, msg));
58+
}
59+
4860
@ExceptionHandler(Exception.class)
4961
public ResponseEntity<ApiResponse<Object>> onOther(Exception ex) {
5062
log.error("unhandled server error", ex);
51-
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ApiResponse.fail(5001, "服务器异常"));
63+
String msg = ex.getMessage() != null ? ex.getMessage() : "服务器异常";
64+
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ApiResponse.fail(5001, msg));
5265
}
5366
}
5467

OpenBlog-business/src/main/java/com/yqz/openblog/media/controller/MediaController.java

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,34 @@
11
package com.yqz.openblog.media.controller;
22

33
import com.yqz.openblog.common.ApiResponse;
4+
import com.yqz.openblog.common.BizException;
45
import com.yqz.openblog.media.dto.MediaCategoryResponse;
56
import com.yqz.openblog.media.dto.MediaListItemResponse;
67
import com.yqz.openblog.media.dto.MediaUploadResponse;
78
import com.yqz.openblog.media.dto.ThumbInfoResponse;
89
import com.yqz.openblog.media.entity.Media;
910
import com.yqz.openblog.media.service.MediaService;
1011
import com.baomidou.mybatisplus.core.metadata.IPage;
12+
import jakarta.servlet.http.HttpServletRequest;
1113
import jakarta.servlet.http.HttpServletResponse;
1214
import org.springframework.http.HttpHeaders;
1315
import org.springframework.http.MediaType;
16+
import org.springframework.util.StringUtils;
1417
import org.springframework.web.bind.annotation.*;
1518
import org.springframework.web.multipart.MultipartFile;
1619

1720
import java.io.IOException;
21+
import java.net.URLDecoder;
22+
import java.nio.charset.StandardCharsets;
1823
import java.util.List;
1924

2025
@RestController
2126
@RequestMapping("/api/v1/media")
2227
@CrossOrigin(origins = "*")
2328
public class MediaController {
2429

30+
private static final String FILES_PATH_MARKER = "/api/v1/media/files/";
31+
2532
private final MediaService mediaService;
2633

2734
public MediaController(MediaService mediaService) {
@@ -59,14 +66,29 @@ public ApiResponse<ThumbInfoResponse> thumbInfo(@PathVariable("key") String key)
5966
return ApiResponse.ok(mediaService.thumbInfo(key));
6067
}
6168

62-
@GetMapping(value = "/files/{key:.+}/thumb", produces = MediaType.ALL_VALUE)
63-
public void serveThumb(@PathVariable("key") String key, HttpServletResponse response) throws IOException {
64-
serveFile(key, true, response);
65-
}
66-
67-
@GetMapping(value = "/files/{key:.+}", produces = MediaType.ALL_VALUE)
68-
public void serveOriginal(@PathVariable("key") String key, HttpServletResponse response) throws IOException {
69-
serveFile(key, false, response);
69+
/**
70+
* 媒体文件访问(原图 / 缩略图)。storageKey 可能含 {@code /}(如 {@code general/uuid.jpg}),
71+
* 须用 {@code /**} 解析路径;Spring Boot 3 默认 PathPattern 下 {@code {key:.+}} 无法跨段匹配。
72+
*/
73+
@GetMapping(value = "/files/**", produces = MediaType.ALL_VALUE)
74+
public void serveFile(HttpServletRequest request, HttpServletResponse response) throws IOException {
75+
String uri = request.getRequestURI();
76+
int idx = uri.indexOf(FILES_PATH_MARKER);
77+
if (idx < 0) {
78+
throw new BizException(4042, "无效媒体路径");
79+
}
80+
String path = uri.substring(idx + FILES_PATH_MARKER.length());
81+
int q = path.indexOf('?');
82+
if (q >= 0) {
83+
path = path.substring(0, q);
84+
}
85+
path = URLDecoder.decode(path, StandardCharsets.UTF_8);
86+
boolean thumb = path.endsWith("/thumb");
87+
String key = thumb ? path.substring(0, path.length() - "/thumb".length()) : path;
88+
if (!StringUtils.hasText(key)) {
89+
throw new BizException(4002, "媒体 key 为空");
90+
}
91+
serveFile(key, thumb, response);
7092
}
7193

7294
private void serveFile(String key, boolean thumb, HttpServletResponse response) throws IOException {

OpenBlog-business/src/main/java/com/yqz/openblog/media/storage/MinioMediaStorage.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,13 @@ private byte[] readObject(String objectName) throws IOException {
102102
if ("NoSuchKey".equals(e.errorResponse().code())) {
103103
throw new BizException(4042, "媒体文件不存在");
104104
}
105-
throw new IOException("读取 MinIO 失败: " + objectName, e);
105+
String detail = e.errorResponse().message() != null ? e.errorResponse().message() : e.errorResponse().code();
106+
throw new IOException("读取 MinIO 失败: " + objectName + " (" + detail + ")", e);
106107
} catch (BizException e) {
107108
throw e;
108109
} catch (Exception e) {
109-
throw new IOException("读取 MinIO 失败: " + objectName, e);
110+
String cause = e.getMessage() != null ? e.getMessage() : e.getClass().getSimpleName();
111+
throw new IOException("读取 MinIO 失败: " + objectName + " (" + cause + ")", e);
110112
}
111113
}
112114

0 commit comments

Comments
 (0)