diff --git a/pom.xml b/pom.xml index 2d909db..2918b77 100644 --- a/pom.xml +++ b/pom.xml @@ -34,6 +34,8 @@ 3.9 1.15 3.0.0 + 8.2.1 + xdclass-cloud @@ -123,6 +125,13 @@ 1.1.0 + + + io.minio + minio + ${minio.version} + + diff --git a/xdclass-common/src/main/java/net/jieyuu/enums/BizCodeEnum.java b/xdclass-common/src/main/java/net/jieyuu/enums/BizCodeEnum.java index c642712..aa50312 100644 --- a/xdclass-common/src/main/java/net/jieyuu/enums/BizCodeEnum.java +++ b/xdclass-common/src/main/java/net/jieyuu/enums/BizCodeEnum.java @@ -27,7 +27,8 @@ public enum BizCodeEnum { */ ACCOUNT_REPEAT(250001, "账号已经存在"), ACCOUNT_UNREGISTER(250002, "账号不存在"), - ACCOUNT_PWD_ERROR(250003, "账号或者密码错误"); + ACCOUNT_PWD_ERROR(250003, "账号或者密码错误"), + FILE_UPLOAD_USER_IMG_FAIL(600101, "用户头像文件上传失败"); @Getter private int code; diff --git a/xdclass-common/src/main/java/net/jieyuu/utils/CommonUtil.java b/xdclass-common/src/main/java/net/jieyuu/utils/CommonUtil.java index e70fd29..5740321 100644 --- a/xdclass-common/src/main/java/net/jieyuu/utils/CommonUtil.java +++ b/xdclass-common/src/main/java/net/jieyuu/utils/CommonUtil.java @@ -5,6 +5,7 @@ import java.net.InetAddress; import java.net.UnknownHostException; import java.security.MessageDigest; import java.util.Random; +import java.util.UUID; public class CommonUtil { @@ -76,6 +77,7 @@ public class CommonUtil { /** * 生成随机数 + * * @param length * @return */ @@ -90,4 +92,17 @@ public class CommonUtil { return stringBuilder.toString(); } + + /** + * 生成UUID + * + * @return + */ + public static String generateUUID() { + return UUID.randomUUID().toString() + //删除- + .replaceAll("-", "") + //截取前32位 + .substring(0, 32); + } } diff --git a/xdclass-user-service/pom.xml b/xdclass-user-service/pom.xml index a820e62..5a194c7 100644 --- a/xdclass-user-service/pom.xml +++ b/xdclass-user-service/pom.xml @@ -30,6 +30,11 @@ spring-boot-starter-mail + + io.minio + minio + + diff --git a/xdclass-user-service/src/main/java/net/jieyuu/component/MinioUtils.java b/xdclass-user-service/src/main/java/net/jieyuu/component/MinioUtils.java new file mode 100644 index 0000000..3ccf610 --- /dev/null +++ b/xdclass-user-service/src/main/java/net/jieyuu/component/MinioUtils.java @@ -0,0 +1,277 @@ +package net.jieyuu.component; + + +import io.minio.*; +import io.minio.messages.DeleteError; +import io.minio.messages.DeleteObject; +import lombok.Data; +import net.jieyuu.utils.CommonUtil; +import org.apache.tomcat.util.http.fileupload.IOUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @description: minio工具类 + * @version:1.0 + */ +@Component +public class MinioUtils { + @Autowired + private MinioClient minioClient; + + @Value("${minio.bucketName}") + private String bucketName; + @Value("${minio.endpoint}") + private String endpoint; + + /** + * description: 判断bucket是否存在,不存在则创建 + * + * @return: void + */ + public void existBucket(String name) { + try { + boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(name).build()); + if (!exists) { + minioClient.makeBucket(MakeBucketArgs.builder().bucket(name).build()); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 创建存储bucket + * + * @param bucketName 存储bucket名称 + * @return Boolean + */ + public Boolean makeBucket(String bucketName) { + try { + minioClient.makeBucket(MakeBucketArgs.builder() + .bucket(bucketName) + .build()); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + return true; + } + + /** + * 删除存储bucket + * + * @param bucketName 存储bucket名称 + * @return Boolean + */ +// public Boolean removeBucket(String bucketName) { +// try { +// minioClient.removeBucket(RemoveBucketArgs.builder() +// .bucket(bucketName) +// .build()); +// } catch (Exception e) { +// e.printStackTrace(); +// return false; +// } +// return true; +// } + + /** + * description: 上传多个文件 + * + * @param multipartFile + * @return: java.lang.String + */ +// public List uploadFiles(MultipartFile[] multipartFile) { +// List names = new ArrayList<>(multipartFile.length); +// for (MultipartFile file : multipartFile) { +// String fileName = file.getOriginalFilename(); +// String[] split = fileName.split("\\."); +// if (split.length > 1) { +// fileName = split[0] + "_" + System.currentTimeMillis() + "." + split[1]; +// } else { +// fileName = fileName + System.currentTimeMillis(); +// } +// InputStream in = null; +// try { +// in = file.getInputStream(); +// minioClient.putObject(PutObjectArgs.builder() +// .bucket(bucketName) +// .object(fileName) +// .stream(in, in.available(), -1) +// .contentType(file.getContentType()) +// .build() +// ); +// } catch (Exception e) { +// e.printStackTrace(); +// } finally { +// if (in != null) { +// try { +// in.close(); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } +// } +// names.add(fileName); +// } +// return names; +// } + + /** + * description: 上传单个文件 + * + * @param file + * @return: String + */ + public String uploadFile(MultipartFile file) { + + String originalFilename = file.getOriginalFilename(); + + LocalDateTime localDateTime = LocalDateTime.now(); + DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd"); + + String folder = dateTimeFormatter.format(localDateTime); + String fileName = CommonUtil.generateUUID(); + + String extension = originalFilename.substring(originalFilename.lastIndexOf(".")); + + String newFileName = "user/" + folder + "/" + fileName + extension; + + + InputStream in = null; + try { + in = file.getInputStream(); + ObjectWriteResponse objectWriteResponse = minioClient.putObject(PutObjectArgs.builder() + .bucket(bucketName) + .object(newFileName) + .stream(in, in.available(), -1) + .contentType(file.getContentType()) + .build() + ); + if (objectWriteResponse != null) { + String res = endpoint + "/" + bucketName + "/" + newFileName; + return res; + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + return null; + } + + /** + * description: 下载文件 + * + * @param fileName + * @return: org.springframework.http.ResponseEntity + */ + public ResponseEntity download(String fileName) { + ResponseEntity responseEntity = null; + InputStream in = null; + ByteArrayOutputStream out = null; + try { + in = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build()); + out = new ByteArrayOutputStream(); + IOUtils.copy(in, out); + //封装返回值 + byte[] bytes = out.toByteArray(); + HttpHeaders headers = new HttpHeaders(); + try { + headers.add("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8")); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + headers.setContentLength(bytes.length); + headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); + headers.setAccessControlExposeHeaders(Arrays.asList("*")); + responseEntity = new ResponseEntity(bytes, headers, HttpStatus.OK); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (out != null) { + out.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + return responseEntity; + } + + /** + * 查看文件对象 + * + * @param bucketName 存储bucket名称 + * @return 存储bucket内文件对象信息 + */ +// public List listObjects(String bucketName) { +// Iterable> results = minioClient.listObjects( +// ListObjectsArgs.builder().bucket(bucketName).build()); +// List objectItems = new ArrayList<>(); +// try { +// for (Result result : results) { +// Item item = result.get(); +// ObjectItem objectItem = new ObjectItem(); +// objectItem.setObjectName(item.objectName()); +// objectItem.setSize(item.size()); +// objectItems.add(objectItem); +// } +// } catch (Exception e) { +// e.printStackTrace(); +// return null; +// } +// return objectItems; +// } +// @Data +// public class ObjectItem { +// private String objectName; +// private Long size; +// } + + /** + * 批量删除文件对象 + * + * @param bucketName 存储bucket名称 + * @param objects 对象名称集合 + */ + public Iterable> removeObjects(String bucketName, List objects) { + List dos = objects.stream().map(e -> new DeleteObject(e)).collect(Collectors.toList()); + Iterable> results = minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(dos).build()); + return results; + } +} \ No newline at end of file diff --git a/xdclass-user-service/src/main/java/net/jieyuu/config/MinIoClientConfig.java b/xdclass-user-service/src/main/java/net/jieyuu/config/MinIoClientConfig.java new file mode 100644 index 0000000..1698276 --- /dev/null +++ b/xdclass-user-service/src/main/java/net/jieyuu/config/MinIoClientConfig.java @@ -0,0 +1,36 @@ +package net.jieyuu.config; + +import io.minio.MinioClient; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Component; + +@Data +@Component +@ConfigurationProperties(prefix = "minio") +@Configuration +public class MinIoClientConfig { + + private String endpoint; + private String accessKeyId; + private String accessKeySecret; + + /** + * 注入minio 客户端 + * + * @return + */ + @Bean + public MinioClient minioClient() { + + return MinioClient.builder() + .endpoint(endpoint) + .credentials(accessKeyId, accessKeySecret) + .build(); + } +} + + + diff --git a/xdclass-user-service/src/main/java/net/jieyuu/controller/AddressController.java b/xdclass-user-service/src/main/java/net/jieyuu/controller/AddressController.java index 1eb4c8f..738cc7f 100644 --- a/xdclass-user-service/src/main/java/net/jieyuu/controller/AddressController.java +++ b/xdclass-user-service/src/main/java/net/jieyuu/controller/AddressController.java @@ -6,7 +6,6 @@ import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import net.jieyuu.service.AddressService; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.*; /** diff --git a/xdclass-user-service/src/main/java/net/jieyuu/controller/UserController.java b/xdclass-user-service/src/main/java/net/jieyuu/controller/UserController.java index f9c895c..ad04a5f 100644 --- a/xdclass-user-service/src/main/java/net/jieyuu/controller/UserController.java +++ b/xdclass-user-service/src/main/java/net/jieyuu/controller/UserController.java @@ -4,13 +4,15 @@ package net.jieyuu.controller; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; +import net.jieyuu.enums.BizCodeEnum; import net.jieyuu.service.AddressService; +import net.jieyuu.service.FileService; +import net.jieyuu.utils.JsonData; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; -import org.springframework.web.bind.annotation.RestController; +import java.io.IOException; /** *

@@ -26,6 +28,8 @@ import org.springframework.web.bind.annotation.RestController; public class UserController { @Autowired AddressService addressService; + @Autowired + FileService fileService; @ApiOperation("根据id查询地址详情") @GetMapping("detail/{address_id}") @@ -35,5 +39,22 @@ public class UserController { return addressService.detail(id); } + /** + * 用户头像上传 + * 默认最大1M,超过报错 + * + * @return + */ + @ApiOperation("用户头像上传") + @PostMapping(value = "upload") + public JsonData uploadUserImg( + @ApiParam(value = "文件上传", required = true) + @RequestPart("file") MultipartFile file) throws IOException { + String result = fileService.uploadUserHeadImg(file); + + return result!=null?JsonData.buildSuccess(result):JsonData.buildResult(BizCodeEnum.FILE_UPLOAD_USER_IMG_FAIL); + } + + } diff --git a/xdclass-user-service/src/main/java/net/jieyuu/service/FileService.java b/xdclass-user-service/src/main/java/net/jieyuu/service/FileService.java new file mode 100644 index 0000000..45cd9bc --- /dev/null +++ b/xdclass-user-service/src/main/java/net/jieyuu/service/FileService.java @@ -0,0 +1,11 @@ +package net.jieyuu.service; + + +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; + +public interface FileService { + public String uploadUserHeadImg(MultipartFile file) throws IOException; + +} diff --git a/xdclass-user-service/src/main/java/net/jieyuu/service/MailService.java b/xdclass-user-service/src/main/java/net/jieyuu/service/MailService.java deleted file mode 100644 index e46548c..0000000 --- a/xdclass-user-service/src/main/java/net/jieyuu/service/MailService.java +++ /dev/null @@ -1,21 +0,0 @@ -package net.jieyuu.service; - - -/** - *

- * 邮件发送类 - *

- * - * @author jieyuu - * @since 2024-02-12 - */ -public interface MailService { - - /** - * 发送邮件 - * @param to - * @param subject - * @param content - */ - public void sendMail(String to, String subject, String content); -} diff --git a/xdclass-user-service/src/main/java/net/jieyuu/service/impl/FileServiceImpl.java b/xdclass-user-service/src/main/java/net/jieyuu/service/impl/FileServiceImpl.java new file mode 100644 index 0000000..82687d9 --- /dev/null +++ b/xdclass-user-service/src/main/java/net/jieyuu/service/impl/FileServiceImpl.java @@ -0,0 +1,62 @@ +package net.jieyuu.service.impl; + +import io.minio.*; +import lombok.extern.slf4j.Slf4j; +import net.jieyuu.service.FileService; +import net.jieyuu.utils.CommonUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.io.InputStream; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + + +@Slf4j +@Service +public class FileServiceImpl implements FileService { + + @Autowired + private MinioClient minioClient; + + @Value("${minio.bucketName}") + private String bucketName; + @Value("${minio.endpoint}") + private String endpoint; + + @Override + public String uploadUserHeadImg(MultipartFile file) { + String originalFilename = file.getOriginalFilename(); + + LocalDateTime localDateTime = LocalDateTime.now(); + + DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd"); + String folder = dateTimeFormatter.format(localDateTime); + + String fileName = CommonUtil.generateUUID(); + String extension = originalFilename.substring(originalFilename.lastIndexOf(".")); + String newFileName = "user/" + folder + "/" + fileName + extension; + + + try (InputStream in = file.getInputStream();){ + + ObjectWriteResponse objectWriteResponse = minioClient.putObject(PutObjectArgs.builder() + .bucket(bucketName) + .object(newFileName) + .stream(in, in.available(), -1) + .contentType(file.getContentType()) + .build() + ); + if (objectWriteResponse != null) { + String res = endpoint + "/" + bucketName + "/" + newFileName; + return res; + } + } catch (Exception e) { + log.error("上传头像失败:{}", e); + } + return null; + } +} \ No newline at end of file diff --git a/xdclass-user-service/src/main/java/net/jieyuu/service/impl/MailServiceImpl.java b/xdclass-user-service/src/main/java/net/jieyuu/service/impl/MailServiceImpl.java deleted file mode 100644 index ab2bd78..0000000 --- a/xdclass-user-service/src/main/java/net/jieyuu/service/impl/MailServiceImpl.java +++ /dev/null @@ -1,44 +0,0 @@ -package net.jieyuu.service.impl; - - -import lombok.extern.slf4j.Slf4j; -import net.jieyuu.service.MailService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.mail.SimpleMailMessage; -import org.springframework.mail.javamail.JavaMailSender; -import org.springframework.stereotype.Service; - -@Service -@Slf4j -public class MailServiceImpl implements MailService { - - /** - * Spring Boot 提供了一个发送邮件的简单抽象,直接注入即可使用 - */ - @Autowired - private JavaMailSender mailSender; - - /** - * 配置文件中的发送邮箱 - */ - @Value("${spring.mail.from}") - private String from; - - @Override - public void sendMail(String to, String subject, String content) { - //创建SimpleMailMessage对象 - SimpleMailMessage message = new SimpleMailMessage(); - //邮件发送人 - message.setFrom(from); - //邮件接收人 - message.setTo(to); - //邮件主题 - message.setSubject(subject); - //邮件内容 - message.setText(content); - //发送邮件 - mailSender.send(message); - log.info("邮件发成功:{}", message.toString()); - } -} \ No newline at end of file diff --git a/xdclass-user-service/src/main/java/net/jieyuu/service/impl/NotifyServiceImpl.java b/xdclass-user-service/src/main/java/net/jieyuu/service/impl/NotifyServiceImpl.java index a74de65..5119e9e 100644 --- a/xdclass-user-service/src/main/java/net/jieyuu/service/impl/NotifyServiceImpl.java +++ b/xdclass-user-service/src/main/java/net/jieyuu/service/impl/NotifyServiceImpl.java @@ -1,8 +1,8 @@ package net.jieyuu.service.impl; import lombok.extern.slf4j.Slf4j; +import net.jieyuu.component.MailService; import net.jieyuu.enums.SendCodeEnum; -import net.jieyuu.service.MailService; import net.jieyuu.service.NotifyService; import net.jieyuu.utils.CheckUtil; import net.jieyuu.utils.CommonUtil; @@ -11,8 +11,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; -import javax.security.auth.Subject; - @Slf4j @Service public class NotifyServiceImpl implements NotifyService { diff --git a/xdclass-user-service/src/main/resources/application.yml b/xdclass-user-service/src/main/resources/application.yml index 5f143e9..735e427 100644 --- a/xdclass-user-service/src/main/resources/application.yml +++ b/xdclass-user-service/src/main/resources/application.yml @@ -36,4 +36,13 @@ mybatis-plus: #设置日志级别,ERROR/WARN/INFO/DEBUG,默认是INFO以上才显示 logging: level: - root: INFO \ No newline at end of file + root: INFO + + +minio: + endpoint: http://134.175.219.253:9000 + access-key-id: Y7sQYzzcBob67Wu7agLK + access-key-secret: PXT1QKl0U23VMMKPmCbDZvGaJiw23AhQWpd4RlO9 + bucketName: xdclass-shop-image + + diff --git a/xdclass-user-service/src/test/java/net/jieyuu/biz/MailTest.java b/xdclass-user-service/src/test/java/net/jieyuu/biz/MailTest.java index e029f5d..d3251c6 100644 --- a/xdclass-user-service/src/test/java/net/jieyuu/biz/MailTest.java +++ b/xdclass-user-service/src/test/java/net/jieyuu/biz/MailTest.java @@ -2,7 +2,7 @@ package net.jieyuu.biz; import lombok.extern.slf4j.Slf4j; import net.jieyuu.UserServiceApplication; -import net.jieyuu.service.MailService; +import net.jieyuu.component.MailService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired;