From 2467a5097c974c76f916facc34241639a6d7bcd2 Mon Sep 17 00:00:00 2001 From: Azure <983547216@qq.com> Date: Mon, 22 Jun 2026 12:56:46 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=B8=AA=E4=BA=BA=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/config/StaticResourceConfig.java | 11 +++++++++-- .../service/impl/UserServiceImpl.java | 17 ++++++++++++++--- .../729bf910-6ca8-43a5-b7d7-5ebecf4db810.jpg | Bin 0 -> 5578 bytes 3 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 uploads/avatars/729bf910-6ca8-43a5-b7d7-5ebecf4db810.jpg diff --git a/src/main/java/com/webgame/webgamebackend/common/config/StaticResourceConfig.java b/src/main/java/com/webgame/webgamebackend/common/config/StaticResourceConfig.java index 158a612..03e1361 100644 --- a/src/main/java/com/webgame/webgamebackend/common/config/StaticResourceConfig.java +++ b/src/main/java/com/webgame/webgamebackend/common/config/StaticResourceConfig.java @@ -8,18 +8,25 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; * 静态资源配置 * * 将本地 uploads/ 目录映射为 /uploads/** URL,用于头像等用户上传文件的访问。 + * 使用绝对路径避免 Tomcat 临时工作目录导致的路径不一致问题。 */ @Configuration public class StaticResourceConfig implements WebMvcConfigurer { + /** 上传文件根目录(相对于项目工作目录) */ + private static final String UPLOAD_DIR = "uploads/"; + /** * 注册静态资源处理器 * - * /uploads/avatars/xxx.jpg → file:./uploads/avatars/xxx.jpg + * /uploads/avatars/xxx.jpg → file:/uploads/avatars/xxx.jpg */ @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { + // 使用正斜杠替换反斜杠,确保 Windows 下 file: URL 格式正确 + String userDir = System.getProperty("user.dir").replace("\\", "/"); + String absoluteUploadPath = "file:" + userDir + "/" + UPLOAD_DIR; registry.addResourceHandler("/uploads/**") - .addResourceLocations("file:./uploads/"); + .addResourceLocations(absoluteUploadPath); } } diff --git a/src/main/java/com/webgame/webgamebackend/service/impl/UserServiceImpl.java b/src/main/java/com/webgame/webgamebackend/service/impl/UserServiceImpl.java index 96c7583..14c95d3 100644 --- a/src/main/java/com/webgame/webgamebackend/service/impl/UserServiceImpl.java +++ b/src/main/java/com/webgame/webgamebackend/service/impl/UserServiceImpl.java @@ -31,6 +31,15 @@ public class UserServiceImpl implements UserService { /** 头像存储目录(相对于项目工作目录) */ private static final String AVATAR_DIR = "uploads/avatars"; + + /** + * 获取头像存储的绝对路径目录 + * + * 使用 user.dir 构建绝对路径,避免 Tomcat 临时工作目录导致 FileNotFoundException。 + */ + private Path getAvatarDir() { + return Paths.get(System.getProperty("user.dir"), AVATAR_DIR).toAbsolutePath(); + } /** 允许的头像文件类型 */ private static final Set ALLOWED_CONTENT_TYPES = Set.of( "image/jpeg", "image/png", "image/webp", "image/gif" @@ -109,8 +118,8 @@ public class UserServiceImpl implements UserService { .orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND)); try { - // 确保目录存在 - Path avatarDir = Paths.get(AVATAR_DIR); + // 确保目录存在(使用绝对路径,避免 Tomcat 临时目录问题) + Path avatarDir = getAvatarDir(); Files.createDirectories(avatarDir); // 删除旧头像文件(如果存在且不是默认 SVG) @@ -163,7 +172,9 @@ public class UserServiceImpl implements UserService { return; } try { - Path oldFile = Paths.get(avatarPath.substring(1)); // 去掉开头的 / + // 从 URL 路径中提取文件名,使用绝对路径删除 + String filename = avatarPath.substring(avatarPath.lastIndexOf('/') + 1); + Path oldFile = getAvatarDir().resolve(filename); Files.deleteIfExists(oldFile); log.debug("[用户服务] 旧头像已删除: {}", oldFile); } catch (IOException e) { diff --git a/uploads/avatars/729bf910-6ca8-43a5-b7d7-5ebecf4db810.jpg b/uploads/avatars/729bf910-6ca8-43a5-b7d7-5ebecf4db810.jpg new file mode 100644 index 0000000000000000000000000000000000000000..791c3be21eaab4300cd0893544984025b587e32b GIT binary patch literal 5578 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 xSGcW28V&sg&aD1CE4^iwNAB6BT{{^1XrE35H literal 0 HcmV?d00001