feat: change the avatar upload controller to separate
This commit is contained in:
parent
b5c32288f7
commit
6c286868f6
33
src/main/java/org/blue/club/controller/AvatarController.java
Normal file
33
src/main/java/org/blue/club/controller/AvatarController.java
Normal file
@ -0,0 +1,33 @@
|
||||
package org.blue.club.controller;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.blue.club.annotation.Auth;
|
||||
import org.blue.club.services.AvatarServices;
|
||||
import org.mmga.spring.boot.starter.entities.Result;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/avatar")
|
||||
@Tag(name = "头像相关接口")
|
||||
@RequiredArgsConstructor
|
||||
public class AvatarController {
|
||||
private final AvatarServices avatarServices;
|
||||
|
||||
@GetMapping("/{sha1}")
|
||||
@Operation(description = "获取头像文件")
|
||||
public void getAvatar(HttpServletResponse response, @Schema(description = "头像文件SHA1值") @PathVariable("sha1") String sha1) {
|
||||
avatarServices.getAvatar(response, sha1);
|
||||
}
|
||||
|
||||
@PostMapping("/upload")
|
||||
@Operation(description = "上传头像")
|
||||
@Auth
|
||||
public Result<String> changeAvatar(MultipartFile file) {
|
||||
return avatarServices.changeAvatar(file);
|
||||
}
|
||||
}
|
@ -54,6 +54,7 @@ public class ClubController {
|
||||
public Result<Boolean> clubRemoveUser(@Auth(permissions = {@PermissionDescription(3), @PermissionDescription(value = 2, type = DescriptionType.CLUB)}) User user, @Schema(description = "社团ID") @RequestParam long clubId, @Schema(description = "需要删除的用户ID") @RequestParam long userId) {
|
||||
return clubServices.clubRemoveUser(user, clubId, userId);
|
||||
}
|
||||
|
||||
@Operation(description = "修改社团用户权限")
|
||||
@PutMapping("/user/auth")
|
||||
public Result<Boolean> clubChangeUserAuth(@Auth(permissions = {@PermissionDescription(3), @PermissionDescription(value = 2, type = DescriptionType.CLUB)}) User user, @RequestBody ClubChangeUserAuthRequest request) {
|
||||
|
@ -0,0 +1,16 @@
|
||||
package org.blue.club.controller;
|
||||
|
||||
import org.blue.club.exceptions.ServerException;
|
||||
import org.mmga.spring.boot.starter.entities.Result;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
|
||||
@ControllerAdvice
|
||||
public class ServerExceptionHandler {
|
||||
@ExceptionHandler({ServerException.class})
|
||||
public ResponseEntity<Result<Void>> handleAuthorizationException(ServerException e) {
|
||||
Result<Void> result = e.getResult();
|
||||
return ResponseEntity.ok(result);
|
||||
}
|
||||
}
|
@ -4,7 +4,6 @@ import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.blue.club.annotation.Auth;
|
||||
@ -17,7 +16,6 @@ import org.mmga.spring.boot.starter.annotation.AuthMapping;
|
||||
import org.mmga.spring.boot.starter.entities.PagerData;
|
||||
import org.mmga.spring.boot.starter.entities.Result;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@ -74,16 +72,9 @@ public class UserController {
|
||||
return userServices.changeAuth(userChangeAuthVo);
|
||||
}
|
||||
|
||||
@PostMapping("/avatar")
|
||||
@Operation(description = "上传头像")
|
||||
@Auth
|
||||
public Result<String> changeAvatar(MultipartFile file) {
|
||||
return userServices.changeAvatar(file);
|
||||
}
|
||||
|
||||
@PutMapping("/avatar")
|
||||
@Operation(description = "修改用户头像")
|
||||
public Result<Boolean> changeAvatar(@RequestParam("code") @Schema(description = "修改头像操作码,可以通过将图片文件上传至([POST] /avatar)接口获取") String avatarOperationCode, @Auth User user) {
|
||||
public Result<Boolean> changeAvatar(@RequestParam("code") @Schema(description = "修改头像操作码,可以通过将图片文件上传至([POST] /api/avatar/upload)接口获取") String avatarOperationCode, @Auth User user) {
|
||||
return userServices.changeAvatar(avatarOperationCode, user);
|
||||
}
|
||||
|
||||
@ -93,12 +84,6 @@ public class UserController {
|
||||
return userServices.getVerifyCode();
|
||||
}
|
||||
|
||||
@GetMapping("/avatar/{sha1}")
|
||||
@Operation(description = "获取头像文件")
|
||||
public void getAvatar(HttpServletResponse response, @Schema(description = "头像文件SHA1值") @PathVariable("sha1") String sha1) {
|
||||
userServices.getAvatar(response, sha1);
|
||||
}
|
||||
|
||||
@GetMapping("/info/{id}")
|
||||
@Operation(description = "获取简略用户信息")
|
||||
public Result<User> getSimpleInfo(@PathVariable("id") int userId) {
|
||||
|
@ -13,4 +13,5 @@ public class ClubVo extends BaseDataVo {
|
||||
private Long id;
|
||||
private String name;
|
||||
private String commit;
|
||||
private String avatar;
|
||||
}
|
||||
|
14
src/main/java/org/blue/club/exceptions/ServerException.java
Normal file
14
src/main/java/org/blue/club/exceptions/ServerException.java
Normal file
@ -0,0 +1,14 @@
|
||||
package org.blue.club.exceptions;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.mmga.spring.boot.starter.entities.Result;
|
||||
|
||||
@Getter
|
||||
public class ServerException extends RuntimeException {
|
||||
private final Result<Void> result;
|
||||
|
||||
public ServerException(Result<Void> result) {
|
||||
super(result.getMsg());
|
||||
this.result = result;
|
||||
}
|
||||
}
|
91
src/main/java/org/blue/club/services/AvatarServices.java
Normal file
91
src/main/java/org/blue/club/services/AvatarServices.java
Normal file
@ -0,0 +1,91 @@
|
||||
package org.blue.club.services;
|
||||
|
||||
import jakarta.servlet.ServletOutputStream;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.tomcat.util.buf.HexUtils;
|
||||
import org.blue.club.dao.redis.AvatarOperationDao;
|
||||
import org.blue.club.entities.vo.data.AvatarTmpVo;
|
||||
import org.blue.club.exceptions.ServerException;
|
||||
import org.blue.club.utils.iop.FileUtils;
|
||||
import org.mmga.spring.boot.starter.entities.Result;
|
||||
import org.mmga.spring.boot.starter.utils.RandomUtils;
|
||||
import org.springframework.http.ContentDisposition;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StreamUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.security.DigestInputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.Date;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AvatarServices {
|
||||
private final RandomUtils randomUtils;
|
||||
private final File tmpFolder;
|
||||
private final File avatarFolder;
|
||||
private final AvatarOperationDao avatarOperationDao;
|
||||
private final FileUtils fileUtils;
|
||||
|
||||
@SneakyThrows
|
||||
public Result<String> changeAvatar(MultipartFile file) {
|
||||
String s = randomUtils.generatorRandomFileName(32);
|
||||
DigestInputStream digestInputStream = new DigestInputStream(file.getInputStream(), DigestUtils.getSha1Digest());
|
||||
File tmpFile = new File(tmpFolder, s);
|
||||
try (FileOutputStream fos = new FileOutputStream(tmpFile)) {
|
||||
digestInputStream.transferTo(fos);
|
||||
}
|
||||
MessageDigest messageDigest = digestInputStream.getMessageDigest();
|
||||
String sha1Hex = HexUtils.toHexString(messageDigest.digest());
|
||||
avatarOperationDao.save(new AvatarTmpVo(s, sha1Hex, new Date()));
|
||||
return Result.success("上传成功", s);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public void getAvatar(HttpServletResponse response, String sha1) {
|
||||
File targetFile = new File(avatarFolder, sha1);
|
||||
if (!targetFile.getAbsolutePath().startsWith(avatarFolder.getAbsolutePath())) {
|
||||
Result.failed(HttpStatus.NOT_FOUND, "文件不存在!").writeToResponse(response);
|
||||
return;
|
||||
}
|
||||
if (!targetFile.exists()) {
|
||||
Result.failed(HttpStatus.NOT_FOUND, "文件不存在!").writeToResponse(response);
|
||||
return;
|
||||
}
|
||||
try (FileInputStream fileInputStream = new FileInputStream(targetFile)) {
|
||||
response.setContentType("image/png");
|
||||
response.setHeader("Content-Disposition", ContentDisposition.attachment().filename(sha1 + ".png").build().toString());
|
||||
ServletOutputStream outputStream = response.getOutputStream();
|
||||
StreamUtils.copy(fileInputStream, outputStream);
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<String> permanentSaveAvatar(String avatarOperationCode) {
|
||||
Optional<AvatarTmpVo> byId = avatarOperationDao.findById(avatarOperationCode);
|
||||
if (byId.isEmpty()) return Optional.empty();
|
||||
AvatarTmpVo avatarTmpVo = byId.get();
|
||||
File tmpFile = new File(tmpFolder, avatarOperationCode);
|
||||
String sha1 = avatarTmpVo.sha1();
|
||||
File targetFile = new File(avatarFolder, sha1);
|
||||
changeAvatarFile(tmpFile, targetFile);
|
||||
avatarOperationDao.deleteById(avatarOperationCode);
|
||||
return Optional.of(sha1);
|
||||
}
|
||||
|
||||
public void changeAvatarFile(File tmpFile, File targetFile) {
|
||||
if (targetFile.exists()) {
|
||||
fileUtils.tryDeleteOrDeleteOnExit(tmpFile);
|
||||
return;
|
||||
}
|
||||
if (!tmpFile.renameTo(targetFile))
|
||||
throw new ServerException(Result.failed(HttpStatus.INTERNAL_SERVER_ERROR, "移动头像文件失败!请联系管理员处理"));
|
||||
}
|
||||
}
|
@ -1,44 +1,26 @@
|
||||
package org.blue.club.services;
|
||||
|
||||
import com.mybatisflex.core.paginate.Page;
|
||||
import jakarta.servlet.ServletOutputStream;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.tomcat.util.buf.HexUtils;
|
||||
import org.blue.club.dao.UserDao;
|
||||
import org.blue.club.dao.redis.AvatarOperationDao;
|
||||
import org.blue.club.dao.redis.VerifyDao;
|
||||
import org.blue.club.entities.dto.user.User;
|
||||
import org.blue.club.entities.dto.user.req.*;
|
||||
import org.blue.club.entities.dto.user.resp.VerifyCodeResponse;
|
||||
import org.blue.club.entities.vo.VerifyVo;
|
||||
import org.blue.club.entities.vo.data.AvatarTmpVo;
|
||||
import org.blue.club.entities.vo.data.UserVo;
|
||||
import org.blue.club.utils.iop.FileUtils;
|
||||
import org.blue.club.utils.iop.VerifyCodeUtils;
|
||||
import org.mmga.spring.boot.starter.entities.PagerData;
|
||||
import org.mmga.spring.boot.starter.entities.Result;
|
||||
import org.mmga.spring.boot.starter.exception.AuthorizationException;
|
||||
import org.mmga.spring.boot.starter.properties.Env;
|
||||
import org.mmga.spring.boot.starter.properties.MainProperties;
|
||||
import org.mmga.spring.boot.starter.utils.RandomUtils;
|
||||
import org.mmga.spring.boot.starter.utils.VoUtils;
|
||||
import org.springframework.http.ContentDisposition;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StreamUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.security.DigestInputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@ -52,13 +34,9 @@ public class UserServices {
|
||||
private final UserDao userDao;
|
||||
private final VerifyDao verifyDao;
|
||||
private final VoUtils voUtils;
|
||||
private final RandomUtils randomUtils;
|
||||
private final File tmpFolder;
|
||||
private final File avatarFolder;
|
||||
private final AvatarOperationDao avatarOperationDao;
|
||||
private final FileUtils fileUtils;
|
||||
private final VerifyCodeUtils verifyCodeUtils;
|
||||
private final MainProperties mainProperties;
|
||||
private final AvatarServices avatarServices;
|
||||
|
||||
private boolean isWrongVerifyCode(String key, String code) {
|
||||
if (mainProperties.getEnv().equals(Env.DEV)) return false;
|
||||
@ -151,70 +129,23 @@ public class UserServices {
|
||||
return Result.success(true);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public Result<String> changeAvatar(MultipartFile file) {
|
||||
String s = randomUtils.generatorRandomFileName(32);
|
||||
DigestInputStream digestInputStream = new DigestInputStream(file.getInputStream(), DigestUtils.getSha1Digest());
|
||||
File tmpFile = new File(tmpFolder, s);
|
||||
try (FileOutputStream fos = new FileOutputStream(tmpFile)) {
|
||||
digestInputStream.transferTo(fos);
|
||||
}
|
||||
MessageDigest messageDigest = digestInputStream.getMessageDigest();
|
||||
String sha1Hex = HexUtils.toHexString(messageDigest.digest());
|
||||
avatarOperationDao.save(new AvatarTmpVo(s, sha1Hex, new Date()));
|
||||
return Result.success("上传成功", s);
|
||||
}
|
||||
|
||||
public Result<Boolean> changeAvatar(String avatarOperationCode, User user) {
|
||||
Optional<AvatarTmpVo> byId = avatarOperationDao.findById(avatarOperationCode);
|
||||
if (byId.isEmpty()) return Result.failed(HttpStatus.NOT_FOUND, "操作码错误或过期");
|
||||
AvatarTmpVo avatarTmpVo = byId.get();
|
||||
File tmpFile = new File(tmpFolder, avatarOperationCode);
|
||||
String sha1 = avatarTmpVo.sha1();
|
||||
File targetFile = new File(avatarFolder, sha1);
|
||||
Optional<Result<Boolean>> booleanResult = changeAvatarFile(tmpFile, targetFile);
|
||||
if (booleanResult.isPresent()) return booleanResult.get();
|
||||
avatarOperationDao.deleteById(avatarOperationCode);
|
||||
Optional<String> result = avatarServices.permanentSaveAvatar(avatarOperationCode);
|
||||
if (result.isEmpty()) return Result.failed(HttpStatus.NOT_FOUND, "操作码错误或过期");
|
||||
UserVo userVo = new UserVo();
|
||||
userVo.setId(user.getId());
|
||||
userVo.setAvatar(sha1);
|
||||
userVo.setAvatar(result.get());
|
||||
userDao.update(userVo);
|
||||
return Result.success(true);
|
||||
}
|
||||
|
||||
public Optional<Result<Boolean>> changeAvatarFile(File tmpFile, File targetFile) {
|
||||
if (targetFile.exists()) {
|
||||
fileUtils.tryDeleteOrDeleteOnExit(tmpFile);
|
||||
return Optional.empty();
|
||||
}
|
||||
if (!tmpFile.renameTo(targetFile))
|
||||
return Optional.of(Result.failed(HttpStatus.INTERNAL_SERVER_ERROR, "移动头像文件失败!请联系管理员处理"));
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public Result<VerifyCodeResponse> getVerifyCode() {
|
||||
VerifyCodeResponse verifyCodeResponse = verifyCodeUtils.generateVerifyCode();
|
||||
return Result.success(verifyCodeResponse);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public void getAvatar(HttpServletResponse response, String sha1) {
|
||||
File targetFile = new File(avatarFolder, sha1);
|
||||
if (!targetFile.getAbsolutePath().startsWith(avatarFolder.getAbsolutePath())) {
|
||||
Result.failed(HttpStatus.NOT_FOUND, "文件不存在!").writeToResponse(response);
|
||||
return;
|
||||
}
|
||||
if (!targetFile.exists()) {
|
||||
Result.failed(HttpStatus.NOT_FOUND, "文件不存在!").writeToResponse(response);
|
||||
return;
|
||||
}
|
||||
try (FileInputStream fileInputStream = new FileInputStream(targetFile)) {
|
||||
response.setContentType("image/png");
|
||||
response.setHeader("Content-Disposition", ContentDisposition.attachment().filename(sha1 + ".png").build().toString());
|
||||
ServletOutputStream outputStream = response.getOutputStream();
|
||||
StreamUtils.copy(fileInputStream, outputStream);
|
||||
}
|
||||
}
|
||||
|
||||
public Result<User> getUserInfo(int userId) {
|
||||
UserVo userVo = userDao.selectOneWithRelationsById(userId);
|
||||
|
Loading…
x
Reference in New Issue
Block a user