diff --git a/commom/pom.xml b/commom/pom.xml index c624595..30f9a0c 100644 --- a/commom/pom.xml +++ b/commom/pom.xml @@ -5,7 +5,7 @@ lx-im com.lx - ${im.version} + 1.0.0 4.0.0 commom diff --git a/commom/src/main/java/com/lx/common/enums/FileTypeEnum.java b/commom/src/main/java/com/lx/common/enums/FileTypeEnum.java new file mode 100644 index 0000000..23b6b01 --- /dev/null +++ b/commom/src/main/java/com/lx/common/enums/FileTypeEnum.java @@ -0,0 +1,39 @@ +package com.lx.common.enums; + +public enum FileTypeEnum { + + FILE(0,"文件"), + IMAGE(1,"图片"), + VIDEO(2,"视频"); + + + private Integer code; + + private String desc; + + FileTypeEnum(Integer index, String desc) { + this.code =index; + this.desc=desc; + } + + public static FileTypeEnum fromCode(Integer code){ + for (FileTypeEnum typeEnum:values()) { + if (typeEnum.code.equals(code)) { + return typeEnum; + } + } + return null; + } + + + public String getDesc() { + return desc; + } + + public Integer getCode(){ + return this.code; + } + + +} + diff --git a/im-platform/pom.xml b/im-platform/pom.xml index dc245de..23b47f0 100644 --- a/im-platform/pom.xml +++ b/im-platform/pom.xml @@ -5,7 +5,7 @@ lx-im com.lx - ${im.version} + 1.0.0 4.0.0 @@ -16,7 +16,7 @@ com.lx commom - ${im.version} + 1.0.0 org.springframework.boot @@ -66,6 +66,38 @@ org.springframework.session spring-session-data-redis + + + io.minio + minio + 8.4.3 + + + com.squareup.okhttp3 + okhttp + + + org.jetbrains.kotlin + kotlin-stdlib + + + + + com.squareup.okhttp3 + okhttp + 4.9.0 + + + org.jetbrains.kotlin + kotlin-stdlib + 1.3.70 + + + + net.coobird + thumbnailator + 0.4.8 + diff --git a/im-platform/src/main/java/com/lx/implatform/config/MinIoClientConfig.java b/im-platform/src/main/java/com/lx/implatform/config/MinIoClientConfig.java new file mode 100644 index 0000000..e1ed793 --- /dev/null +++ b/im-platform/src/main/java/com/lx/implatform/config/MinIoClientConfig.java @@ -0,0 +1,31 @@ +package com.lx.implatform.config; + +import io.minio.MinioClient; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + + +@Configuration +public class MinIoClientConfig { + @Value("${minio.endpoint}") + private String endpoint; + @Value("${minio.accessKey}") + private String accessKey; + @Value("${minio.secretKey}") + private String secretKey; + + /** + * 注入minio 客户端 + * @return + */ + @Bean + public MinioClient minioClient(){ + + MinioClient client = MinioClient.builder() + .endpoint(endpoint) + .credentials(accessKey, secretKey) + .build(); + return client; + } +} \ No newline at end of file diff --git a/im-platform/src/main/java/com/lx/implatform/config/SwaggerConfig.java b/im-platform/src/main/java/com/lx/implatform/config/SwaggerConfig.java index c6866ac..39a1857 100644 --- a/im-platform/src/main/java/com/lx/implatform/config/SwaggerConfig.java +++ b/im-platform/src/main/java/com/lx/implatform/config/SwaggerConfig.java @@ -32,9 +32,9 @@ public class SwaggerConfig { private ApiInfo apiInfo() { return new ApiInfoBuilder() - .title("uaa service Doc") - .description("用户授权服务Api文档") - .termsOfServiceUrl("http://www.baidu.com/") + .title("IM Platform doc") + .description("蓝星IM API文档") + .termsOfServiceUrl("http://XXX/") .version("1.0") .build(); } diff --git a/im-platform/src/main/java/com/lx/implatform/config/WebSecurityConfg.java b/im-platform/src/main/java/com/lx/implatform/config/WebSecurityConfg.java index 80f36e1..cb0c14c 100644 --- a/im-platform/src/main/java/com/lx/implatform/config/WebSecurityConfg.java +++ b/im-platform/src/main/java/com/lx/implatform/config/WebSecurityConfg.java @@ -55,7 +55,7 @@ public class WebSecurityConfg extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() - .antMatchers("/login","/logout","/register","/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**") + .antMatchers("/image/upload","/login","/logout","/register","/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**") .permitAll() .anyRequest() //任何其它请求 .authenticated() //都需要身份认证 diff --git a/im-platform/src/main/java/com/lx/implatform/controller/FileController.java b/im-platform/src/main/java/com/lx/implatform/controller/FileController.java new file mode 100644 index 0000000..13b4be6 --- /dev/null +++ b/im-platform/src/main/java/com/lx/implatform/controller/FileController.java @@ -0,0 +1,42 @@ +package com.lx.implatform.controller; + +import com.lx.common.result.Result; +import com.lx.common.result.ResultUtils; +import com.lx.implatform.service.thirdparty.FileService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +/* + * 文件上传 + * @Author Blue + * @Date 2022/10/28 + */ +@Slf4j +@RestController +@Api(tags = "文件上传") +public class FileController { + + + @Autowired + private FileService fileService; + + @ApiOperation(value = "上传图片",notes="上传图片") + @PostMapping("/image/upload") + public Result upload(MultipartFile file) { + return ResultUtils.success(fileService.uploadImage(file)); + } + + + @GetMapping("/online") + @ApiOperation(value = "查找用户",notes="根据昵称查找用户") + public Result checkOnline(){ + + return ResultUtils.success(""); + } +} \ No newline at end of file diff --git a/im-platform/src/main/java/com/lx/implatform/controller/FriendsController.java b/im-platform/src/main/java/com/lx/implatform/controller/FriendsController.java index 0a1c7ac..dbb2c64 100644 --- a/im-platform/src/main/java/com/lx/implatform/controller/FriendsController.java +++ b/im-platform/src/main/java/com/lx/implatform/controller/FriendsController.java @@ -17,7 +17,7 @@ import javax.validation.constraints.NotEmpty; import java.util.List; import java.util.stream.Collectors; -@Api(tags = "好友相关API") +@Api(tags = "好友") @RestController @RequestMapping("/friends") public class FriendsController { diff --git a/im-platform/src/main/java/com/lx/implatform/controller/SingleMessageController.java b/im-platform/src/main/java/com/lx/implatform/controller/SingleMessageController.java index f4d1b47..aa36f26 100644 --- a/im-platform/src/main/java/com/lx/implatform/controller/SingleMessageController.java +++ b/im-platform/src/main/java/com/lx/implatform/controller/SingleMessageController.java @@ -15,7 +15,7 @@ import org.springframework.web.bind.annotation.RestController; import javax.validation.Valid; -@Api(tags = "单发消息相关api") +@Api(tags = "私聊消息") @RestController @RequestMapping("/message/single") public class SingleMessageController { diff --git a/im-platform/src/main/java/com/lx/implatform/controller/UserController.java b/im-platform/src/main/java/com/lx/implatform/controller/UserController.java index 9ad4e39..8896ea5 100644 --- a/im-platform/src/main/java/com/lx/implatform/controller/UserController.java +++ b/im-platform/src/main/java/com/lx/implatform/controller/UserController.java @@ -18,7 +18,7 @@ import javax.validation.constraints.NotEmpty; import java.util.List; -@Api(tags = "用户相关API") +@Api(tags = "用户") @RestController @RequestMapping("/user") public class UserController { @@ -28,12 +28,14 @@ public class UserController { @GetMapping("/online") + @ApiOperation(value = "判断用户是否在线",notes="返回在线的用户id集合") public Result checkOnline(@NotEmpty @RequestParam("userIds") String userIds){ List onlineIds = userService.checkOnline(userIds); return ResultUtils.success(onlineIds); } @GetMapping("/self") + @ApiOperation(value = "获取当前用户信息",notes="获取当前用户信息") public Result findSelfInfo(){ UserSession session = SessionContext.getSession(); User user = userService.getById(session.getId()); @@ -43,6 +45,7 @@ public class UserController { @GetMapping("/find/{id}") + @ApiOperation(value = "查找用户",notes="根据id查找用户") public Result findByIde(@NotEmpty @PathVariable("id") long id){ User user = userService.getById(id); UserVO userVO = BeanUtils.copyProperties(user,UserVO.class); @@ -51,8 +54,8 @@ public class UserController { @GetMapping("/findByNickName") - @ApiOperation(value = "查找非好友用户",notes="查找非好友用户") - public Result findUnfriendsUser(@NotEmpty(message = "用户昵称不可为空") @RequestParam("nickName") String nickName){ + @ApiOperation(value = "查找用户",notes="根据昵称查找用户") + public Result findByNickName(@NotEmpty(message = "用户昵称不可为空") @RequestParam("nickName") String nickName){ return ResultUtils.success( userService.findUserByNickName(nickName)); } } diff --git a/im-platform/src/main/java/com/lx/implatform/service/thirdparty/FileService.java b/im-platform/src/main/java/com/lx/implatform/service/thirdparty/FileService.java new file mode 100644 index 0000000..a727324 --- /dev/null +++ b/im-platform/src/main/java/com/lx/implatform/service/thirdparty/FileService.java @@ -0,0 +1,83 @@ +package com.lx.implatform.service.thirdparty; + +import com.lx.common.enums.FileTypeEnum; +import com.lx.common.enums.ResultCode; +import com.lx.implatform.exception.GlobalException; +import com.lx.implatform.util.ImageUtil; +import com.lx.implatform.util.MinioUtil; +import com.lx.implatform.vo.UploadImageVO; +import lombok.extern.slf4j.Slf4j; +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 javax.annotation.PostConstruct; +import java.io.IOException; + +/* + * 文件上传服务 + * @Author Blue + * @Date 2022/10/28 + */ +@Slf4j +@Service +public class FileService { + + @Autowired + private MinioUtil minioUtil; + + @Value("${minio.public}") + private String minIOServer; + @Value("${minio.bucketName}") + private String bucketName; + + @Value("${minio.imagePath}") + private String imagePath; + + @Value("${minio.filePath}") + private String filePath; + + @PostConstruct + public void init(){ + if(!minioUtil.bucketExists(bucketName)){ + minioUtil.makeBucket(bucketName); + } + } + + public UploadImageVO uploadImage(MultipartFile file){ + try { + UploadImageVO vo = new UploadImageVO(); + + String fileName = minioUtil.upload(bucketName,imagePath,file); + vo.setOriginUrl(generUrl(FileTypeEnum.IMAGE,fileName)); + // 上传缩略图 + byte[] imageByte = ImageUtil.compressForScale(file.getBytes(),100); + fileName = minioUtil.upload(bucketName,imagePath,file.getOriginalFilename(),imageByte,file.getContentType()); + vo.setCompressUrl(generUrl(FileTypeEnum.IMAGE,fileName)); + return vo; + } catch (IOException e) { + log.error("上传图片失败,{}",e.getMessage(),e); + throw new GlobalException(ResultCode.PROGRAM_ERROR,"图片上传失败"); + } + } + + + public String generUrl(FileTypeEnum fileTypeEnum, String fileName){ + String url = minIOServer+"/"+bucketName; + switch (fileTypeEnum){ + case FILE: + url += "/file/"; + break; + case IMAGE: + url += "/image/"; + break; + case VIDEO: + url += "/video/"; + break; + } + url += fileName; + return url; + } + +} diff --git a/im-platform/src/main/java/com/lx/implatform/util/ImageUtil.java b/im-platform/src/main/java/com/lx/implatform/util/ImageUtil.java new file mode 100644 index 0000000..a942c56 --- /dev/null +++ b/im-platform/src/main/java/com/lx/implatform/util/ImageUtil.java @@ -0,0 +1,78 @@ +package com.lx.implatform.util; + + +import lombok.extern.slf4j.Slf4j; +import net.coobird.thumbnailator.Thumbnails; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; + +@Slf4j +public class ImageUtil { + + //以下是常量,按照阿里代码开发规范,不允许代码中出现魔法值 + private static final Integer ZERO = 0; + private static final Integer ONE_ZERO_TWO_FOUR = 1024; + private static final Integer NINE_ZERO_ZERO = 900; + private static final Integer THREE_TWO_SEVEN_FIVE = 3275; + private static final Integer TWO_ZERO_FOUR_SEVEN = 2047; + private static final Double ZERO_EIGHT_FIVE = 0.85; + private static final Double ZERO_SIX = 0.6; + private static final Double ZERO_FOUR_FOUR = 0.44; + private static final Double ZERO_FOUR = 0.4; + + /** + * 根据指定大小压缩图片 + * + * @param imageBytes 源图片字节数组 + * @param desFileSize 指定图片大小,单位kb + * @return 压缩质量后的图片字节数组 + */ + public static byte[] compressForScale(byte[] imageBytes, long desFileSize) { + if (imageBytes == null || imageBytes.length <= ZERO || imageBytes.length < desFileSize * ONE_ZERO_TWO_FOUR) { + return imageBytes; + } + long srcSize = imageBytes.length; + double accuracy = getAccuracy(srcSize / ONE_ZERO_TWO_FOUR); + try { + while (imageBytes.length > desFileSize * ONE_ZERO_TWO_FOUR) { + ByteArrayInputStream inputStream = new ByteArrayInputStream(imageBytes); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(imageBytes.length); + Thumbnails.of(inputStream) + .scale(accuracy) + .outputQuality(accuracy) + .toOutputStream(outputStream); + imageBytes = outputStream.toByteArray(); + } + log.info("图片原大小={}kb | 压缩后大小={}kb", + srcSize / ONE_ZERO_TWO_FOUR, imageBytes.length / ONE_ZERO_TWO_FOUR); + } catch (Exception e) { + log.error("【图片压缩】msg=图片压缩失败!", e); + } + return imageBytes; + } + + /** + * 自动调节精度(经验数值) + * + * @param size 源图片大小 + * @return 图片压缩质量比 + */ + private static double getAccuracy(long size) { + double accuracy; + if (size < NINE_ZERO_ZERO) { + accuracy = ZERO_EIGHT_FIVE; + } else if (size < TWO_ZERO_FOUR_SEVEN) { + accuracy = ZERO_SIX; + } else if (size < THREE_TWO_SEVEN_FIVE) { + accuracy = ZERO_FOUR_FOUR; + } else { + accuracy = ZERO_FOUR; + } + return accuracy; + } + +} + + + diff --git a/im-platform/src/main/java/com/lx/implatform/util/MinioUtil.java b/im-platform/src/main/java/com/lx/implatform/util/MinioUtil.java new file mode 100644 index 0000000..2de97d9 --- /dev/null +++ b/im-platform/src/main/java/com/lx/implatform/util/MinioUtil.java @@ -0,0 +1,139 @@ +package com.lx.implatform.util; + + +import com.lx.common.util.DateTimeUtils; +import io.minio.*; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.Date; + +@Component +public class MinioUtil { + + + @Autowired + private MinioClient minioClient; + + /** + * 查看存储bucket是否存在 + * @return boolean + */ + public Boolean bucketExists(String bucketName) { + Boolean found; + try { + found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + return found; + } + + /** + * 创建存储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 + * @return Boolean + */ + public Boolean removeBucket(String bucketName) { + try { + minioClient.removeBucket(RemoveBucketArgs.builder() + .bucket(bucketName) + .build()); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + return true; + } + + + /** + * 文件上传 + * @bucketName bucket名称 + * @path 路径 + * @param file 文件 + * @return Boolean + */ + public String upload(String bucketName,String path,MultipartFile file) { + String originalFilename = file.getOriginalFilename(); + if (StringUtils.isBlank(originalFilename)){ + throw new RuntimeException(); + } + String fileName = System.currentTimeMillis() + originalFilename.substring(originalFilename.lastIndexOf(".")); + String objectName = DateTimeUtils.getFormatDate(new Date(),DateTimeUtils.PARTDATEFORMAT)+ "/" + fileName; + try { + PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(bucketName).object(path+"/" +objectName) + .stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType()).build(); + //文件名称相同会覆盖 + minioClient.putObject(objectArgs); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + return objectName; + } + + /** + * 文件上传 + * @param bucketName bucket名称 + * @param path 路径 + * @param name 文件名 + * @param fileByte 文件内容 + * @param contentType + * @return Boolean + */ + public String upload(String bucketName,String path,String name,byte[] fileByte,String contentType) { + + String fileName = System.currentTimeMillis() + name.substring(name.lastIndexOf(".")); + String objectName = DateTimeUtils.getFormatDate(new Date(),DateTimeUtils.PARTDATEFORMAT)+ "/" + fileName; + try { + InputStream stream = new ByteArrayInputStream(fileByte); + PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(bucketName).object(path+"/" +objectName) + .stream(stream, fileByte.length, -1).contentType(contentType).build(); + //文件名称相同会覆盖 + minioClient.putObject(objectArgs); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + return objectName; + } + + + /** + * 删除 + * @param bucketName bucket名称 + * @path path + * @param fileName + * @return + * @throws Exception + */ + public boolean remove(String bucketName,String path,String fileName){ + try { + minioClient.removeObject( RemoveObjectArgs.builder().bucket(bucketName).object(path+fileName).build()); + }catch (Exception e){ + return false; + } + return true; + } +} diff --git a/im-platform/src/main/java/com/lx/implatform/vo/UploadImageVO.java b/im-platform/src/main/java/com/lx/implatform/vo/UploadImageVO.java new file mode 100644 index 0000000..40fd3ee --- /dev/null +++ b/im-platform/src/main/java/com/lx/implatform/vo/UploadImageVO.java @@ -0,0 +1,16 @@ +package com.lx.implatform.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +@ApiModel("图片上传VO") +public class UploadImageVO { + + @ApiModelProperty(value = "原图") + private String originUrl; + + @ApiModelProperty(value = "缩略图") + private String compressUrl; +} diff --git a/im-platform/src/main/resources/application.yml b/im-platform/src/main/resources/application.yml index 410fd2d..3ab7486 100644 --- a/im-platform/src/main/resources/application.yml +++ b/im-platform/src/main/resources/application.yml @@ -23,6 +23,14 @@ mybatis-plus: mapper-locations: # *.xml的具体路径 - classpath*:mapper/*.xml +minio: + endpoint: http://127.0.0.1:9001 + public: http://127.0.0.1:9001 + accessKey: admin + secretKey: 12345678 + bucketName: lx-im + imagePath: image + filePath: file web-ui: login-page: http://localhost:8080 diff --git a/im-server/pom.xml b/im-server/pom.xml index 8189fec..998752a 100644 --- a/im-server/pom.xml +++ b/im-server/pom.xml @@ -5,7 +5,7 @@ lx-im com.lx - ${im.version} + 1.0.0 4.0.0 @@ -16,7 +16,7 @@ com.lx commom - ${im.version} + 1.0.0 org.springframework.boot diff --git a/im-ui/src/components/AddFriends.vue b/im-ui/src/components/AddFriends.vue index 9dbbd6b..c39a102 100644 --- a/im-ui/src/components/AddFriends.vue +++ b/im-ui/src/components/AddFriends.vue @@ -1,22 +1,22 @@ - + - - - - - + + + + + + + + {{userInfo.nickName}} + {{ userInfo.online?"[在线]":"[离线]"}} + + 添加 + 已添加 - - {{userInfo.nickName}} - {{ userInfo.online?"[在线]":"[离线]"}} - - 添加 - 已添加 - @@ -88,6 +88,7 @@