用户头像更换后移除旧头像文件

This commit is contained in:
RuoYi
2025-06-06 19:36:36 +08:00
parent 8aca11c2a2
commit c86bfa9243
13 changed files with 144 additions and 33 deletions

View File

@@ -2,7 +2,9 @@ package com.ruoyi.system.api;
import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.core.constant.ServiceNameConstants; import com.ruoyi.common.core.constant.ServiceNameConstants;
@@ -26,4 +28,13 @@ public interface RemoteFileService
*/ */
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public R<SysFile> upload(@RequestPart(value = "file") MultipartFile file); public R<SysFile> upload(@RequestPart(value = "file") MultipartFile file);
/**
* 删除文件
*
* @param fileUrl 文件地址
* @return 结果
*/
@DeleteMapping(value = "/delete", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public R<Boolean> delete(@RequestParam("fileUrl") String fileUrl);
} }

View File

@@ -30,6 +30,12 @@ public class RemoteFileFallbackFactory implements FallbackFactory<RemoteFileServ
{ {
return R.fail("上传文件失败:" + throwable.getMessage()); return R.fail("上传文件失败:" + throwable.getMessage());
} }
@Override
public R<Boolean> delete(String fileUrl)
{
return R.fail("删除文件失败:" + throwable.getMessage());
}
}; };
} }
} }

View File

@@ -114,20 +114,20 @@ public class FileUtils
} }
/** /**
* 检查文件是否可下载 * 校验文件路径合法性(安全性与扩展名)
* *
* @param resource 需要下载的文件 * @param fileUrl 待校验的文件地址
* @return true 正常 false 非法 * @return true 正常 false 非法
*/ */
public static boolean checkAllowDownload(String resource) public static boolean validateFilePath(String fileUrl)
{ {
// 禁止目录上跳级别 // 禁止目录上跳级别
if (StringUtils.contains(resource, "..")) if (StringUtils.contains(fileUrl, ".."))
{ {
return false; return false;
} }
// 判断是否在允许下载的文件规则内 // 判断是否在允许下载的文件规则内
return ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource)); return ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(fileUrl));
} }
/** /**

View File

@@ -3,10 +3,12 @@ package com.ruoyi.file.controller;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.utils.file.FileUtils; import com.ruoyi.common.core.utils.file.FileUtils;
import com.ruoyi.file.service.ISysFileService; import com.ruoyi.file.service.ISysFileService;
import com.ruoyi.system.api.domain.SysFile; import com.ruoyi.system.api.domain.SysFile;
@@ -45,4 +47,26 @@ public class SysFileController
return R.fail(e.getMessage()); return R.fail(e.getMessage());
} }
} }
/**
* 文件删除请求
*/
@DeleteMapping("delete")
public R<Boolean> delete(String fileUrl)
{
try
{
if (!FileUtils.validateFilePath(fileUrl))
{
throw new Exception(StringUtils.format("资源文件({})非法,不允许删除。 ", fileUrl));
}
sysFileService.deleteFile(fileUrl);
return R.ok();
}
catch (Exception e)
{
log.error("删除文件失败", e);
return R.fail(e.getMessage());
}
}
} }

View File

@@ -1,11 +1,11 @@
package com.ruoyi.file.service; package com.ruoyi.file.service;
import java.io.InputStream; import java.io.InputStream;
import com.alibaba.nacos.common.utils.IoUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import com.alibaba.nacos.common.utils.IoUtils;
import com.github.tobato.fastdfs.domain.fdfs.StorePath; import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.service.FastFileStorageClient; import com.github.tobato.fastdfs.service.FastFileStorageClient;
import com.ruoyi.common.core.utils.file.FileTypeUtils; import com.ruoyi.common.core.utils.file.FileTypeUtils;
@@ -53,4 +53,24 @@ public class FastDfsSysFileServiceImpl implements ISysFileService
IoUtils.closeQuietly(inputStream); IoUtils.closeQuietly(inputStream);
} }
} }
/**
* FastDFS文件删除接口
*
* @param fileUrl 文件访问URL
* @throws Exception
*/
@Override
public void deleteFile(String fileUrl) throws Exception
{
try
{
StorePath storePath = StorePath.parseFromUrl(fileUrl);
storageClient.deleteFile(storePath.getGroup(), storePath.getPath());
}
catch (Exception e)
{
throw new RuntimeException("FastDfs Failed to delete file: ", e);
}
}
} }

View File

@@ -17,4 +17,12 @@ public interface ISysFileService
* @throws Exception * @throws Exception
*/ */
public String uploadFile(MultipartFile file) throws Exception; public String uploadFile(MultipartFile file) throws Exception;
/**
* 文件删除接口
*
* @param fileUrl 文件访问URL
* @throws Exception
*/
public void deleteFile(String fileUrl) throws Exception;
} }

View File

@@ -4,6 +4,8 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.utils.file.FileUtils;
import com.ruoyi.file.utils.FileUploadUtils; import com.ruoyi.file.utils.FileUploadUtils;
/** /**
@@ -47,4 +49,17 @@ public class LocalSysFileServiceImpl implements ISysFileService
String url = domain + localFilePrefix + name; String url = domain + localFilePrefix + name;
return url; return url;
} }
/**
* 本地文件删除接口
*
* @param fileUrl 文件访问URL
* @throws Exception
*/
@Override
public void deleteFile(String fileUrl) throws Exception
{
String localFile = StringUtils.substringAfter(fileUrl, localFilePrefix);
FileUtils.deleteFile(localFilePath + localFile);
}
} }

View File

@@ -5,10 +5,12 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import com.alibaba.nacos.common.utils.IoUtils; import com.alibaba.nacos.common.utils.IoUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.file.config.MinioConfig; import com.ruoyi.file.config.MinioConfig;
import com.ruoyi.file.utils.FileUploadUtils; import com.ruoyi.file.utils.FileUploadUtils;
import io.minio.MinioClient; import io.minio.MinioClient;
import io.minio.PutObjectArgs; import io.minio.PutObjectArgs;
import io.minio.RemoveObjectArgs;
/** /**
* Minio 文件存储 * Minio 文件存储
@@ -57,4 +59,24 @@ public class MinioSysFileServiceImpl implements ISysFileService
IoUtils.closeQuietly(inputStream); IoUtils.closeQuietly(inputStream);
} }
} }
/**
* Minio文件删除接口
*
* @param fileUrl 文件访问URL
* @throws Exception
*/
@Override
public void deleteFile(String fileUrl) throws Exception
{
try
{
String minioFile = StringUtils.substringAfter(fileUrl, minioConfig.getBucketName());
client.removeObject(RemoveObjectArgs.builder().bucket(minioConfig.getBucketName()).object(minioFile).build());
}
catch (Exception e)
{
throw new RuntimeException("Minio Failed to delete file", e);
}
}
} }

View File

@@ -100,7 +100,7 @@ public class SysProfileController extends BaseController
String oldPassword = params.get("oldPassword"); String oldPassword = params.get("oldPassword");
String newPassword = params.get("newPassword"); String newPassword = params.get("newPassword");
LoginUser loginUser = SecurityUtils.getLoginUser(); LoginUser loginUser = SecurityUtils.getLoginUser();
String userName = loginUser.getUsername(); Long userId = loginUser.getUserid();
String password = loginUser.getSysUser().getPassword(); String password = loginUser.getSysUser().getPassword();
if (!SecurityUtils.matchesPassword(oldPassword, password)) if (!SecurityUtils.matchesPassword(oldPassword, password))
{ {
@@ -111,7 +111,7 @@ public class SysProfileController extends BaseController
return error("新密码不能与旧密码相同"); return error("新密码不能与旧密码相同");
} }
newPassword = SecurityUtils.encryptPassword(newPassword); newPassword = SecurityUtils.encryptPassword(newPassword);
if (userService.resetUserPwd(userName, newPassword) > 0) if (userService.resetUserPwd(userId, newPassword) > 0)
{ {
// 更新缓存用户密码&密码最后更新时间 // 更新缓存用户密码&密码最后更新时间
loginUser.getSysUser().setPwdUpdateDate(DateUtils.getNowDate()); loginUser.getSysUser().setPwdUpdateDate(DateUtils.getNowDate());
@@ -143,8 +143,13 @@ public class SysProfileController extends BaseController
return error("文件服务异常,请联系管理员"); return error("文件服务异常,请联系管理员");
} }
String url = fileResult.getData().getUrl(); String url = fileResult.getData().getUrl();
if (userService.updateUserAvatar(loginUser.getUsername(), url)) if (userService.updateUserAvatar(loginUser.getUserid(), url))
{ {
String oldAvatarUrl = loginUser.getSysUser().getAvatar();
if (StringUtils.isNotEmpty(oldAvatarUrl))
{
remoteFileService.delete(oldAvatarUrl);
}
AjaxResult ajax = AjaxResult.success(); AjaxResult ajax = AjaxResult.success();
ajax.put("imgUrl", url); ajax.put("imgUrl", url);
// 更新缓存用户头像 // 更新缓存用户头像

View File

@@ -70,20 +70,20 @@ public interface SysUserMapper
/** /**
* 修改用户头像 * 修改用户头像
* *
* @param userName 用户 * @param userId 用户ID
* @param avatar 头像地址 * @param avatar 头像地址
* @return 结果 * @return 结果
*/ */
public int updateUserAvatar(@Param("userName") String userName, @Param("avatar") String avatar); public int updateUserAvatar(@Param("userId") Long userId, @Param("avatar") String avatar);
/** /**
* 重置用户密码 * 重置用户密码
* *
* @param userName 用户 * @param userId 用户ID
* @param password 密码 * @param password 密码
* @return 结果 * @return 结果
*/ */
public int resetUserPwd(@Param("userName") String userName, @Param("password") String password); public int resetUserPwd(@Param("userId") Long userId, @Param("password") String password);
/** /**
* 通过用户ID删除用户 * 通过用户ID删除用户

View File

@@ -155,11 +155,11 @@ public interface ISysUserService
/** /**
* 修改用户头像 * 修改用户头像
* *
* @param userName 用户 * @param userId 用户ID
* @param avatar 头像地址 * @param avatar 头像地址
* @return 结果 * @return 结果
*/ */
public boolean updateUserAvatar(String userName, String avatar); public boolean updateUserAvatar(Long userId, String avatar);
/** /**
* 重置用户密码 * 重置用户密码
@@ -172,11 +172,11 @@ public interface ISysUserService
/** /**
* 重置用户密码 * 重置用户密码
* *
* @param userName 用户 * @param userId 用户ID
* @param password 密码 * @param password 密码
* @return 结果 * @return 结果
*/ */
public int resetUserPwd(String userName, String password); public int resetUserPwd(Long userId, String password);
/** /**
* 通过用户ID删除用户 * 通过用户ID删除用户

View File

@@ -344,14 +344,14 @@ public class SysUserServiceImpl implements ISysUserService
/** /**
* 修改用户头像 * 修改用户头像
* *
* @param userName 用户 * @param userId 用户ID
* @param avatar 头像地址 * @param avatar 头像地址
* @return 结果 * @return 结果
*/ */
@Override @Override
public boolean updateUserAvatar(String userName, String avatar) public boolean updateUserAvatar(Long userId, String avatar)
{ {
return userMapper.updateUserAvatar(userName, avatar) > 0; return userMapper.updateUserAvatar(userId, avatar) > 0;
} }
/** /**
@@ -369,14 +369,14 @@ public class SysUserServiceImpl implements ISysUserService
/** /**
* 重置用户密码 * 重置用户密码
* *
* @param userName 用户 * @param userId 用户ID
* @param password 密码 * @param password 密码
* @return 结果 * @return 结果
*/ */
@Override @Override
public int resetUserPwd(String userName, String password) public int resetUserPwd(Long userId, String password)
{ {
return userMapper.resetUserPwd(userName, password); return userMapper.resetUserPwd(userId, password);
} }
/** /**

View File

@@ -19,13 +19,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="loginIp" column="login_ip" /> <result property="loginIp" column="login_ip" />
<result property="loginDate" column="login_date" /> <result property="loginDate" column="login_date" />
<result property="pwdUpdateDate" column="pwd_update_date" /> <result property="pwdUpdateDate" column="pwd_update_date" />
<result property="createBy" column="create_by" /> <result property="createBy" column="create_by" />
<result property="createTime" column="create_time" /> <result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" /> <result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" /> <result property="updateTime" column="update_time" />
<result property="remark" column="remark" /> <result property="remark" column="remark" />
<association property="dept" javaType="SysDept" resultMap="deptResult" /> <association property="dept" javaType="SysDept" resultMap="deptResult" />
<collection property="roles" javaType="java.util.List" resultMap="RoleResult" /> <collection property="roles" javaType="java.util.List" resultMap="RoleResult" />
</resultMap> </resultMap>
<resultMap id="deptResult" type="SysDept"> <resultMap id="deptResult" type="SysDept">
@@ -202,11 +202,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</update> </update>
<update id="updateUserAvatar" parameterType="SysUser"> <update id="updateUserAvatar" parameterType="SysUser">
update sys_user set avatar = #{avatar} where user_name = #{userName} update sys_user set avatar = #{avatar} where user_id = #{userId}
</update> </update>
<update id="resetUserPwd" parameterType="SysUser"> <update id="resetUserPwd" parameterType="SysUser">
update sys_user set pwd_update_date = sysdate(), password = #{password} where user_name = #{userName} update sys_user set pwd_update_date = sysdate(), password = #{password} where user_id = #{userId}
</update> </update>
<delete id="deleteUserById" parameterType="Long"> <delete id="deleteUserById" parameterType="Long">