diff --git a/admin-ui/src/auto/components/WFileUploader.vue b/admin-ui/src/auto/components/WFileUploader.vue new file mode 100644 index 0000000..04605fe --- /dev/null +++ b/admin-ui/src/auto/components/WFileUploader.vue @@ -0,0 +1,653 @@ + + + \ No newline at end of file diff --git a/admin-ui/src/auto/components/WImageUploader.vue b/admin-ui/src/auto/components/WImageUploader.vue new file mode 100644 index 0000000..eeaa546 --- /dev/null +++ b/admin-ui/src/auto/components/WImageUploader.vue @@ -0,0 +1,527 @@ + + + \ No newline at end of file diff --git a/admin-ui/src/auto/components/WImageView.vue b/admin-ui/src/auto/components/WImageView.vue index 445f03f..786e9c2 100644 --- a/admin-ui/src/auto/components/WImageView.vue +++ b/admin-ui/src/auto/components/WImageView.vue @@ -1,9 +1,5 @@ - - \ No newline at end of file diff --git a/admin-ui/src/views/demo/file/index.vue b/admin-ui/src/views/demo/file/index.vue new file mode 100644 index 0000000..667de97 --- /dev/null +++ b/admin-ui/src/views/demo/file/index.vue @@ -0,0 +1,159 @@ + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index ee0ac58..179cd96 100644 --- a/pom.xml +++ b/pom.xml @@ -275,21 +275,20 @@ ruoyi-system-cron ${ruoyi-vue-plus.version} - com.ruoyi - ruoyi-common + ruoyi-system-file ${ruoyi-vue-plus.version} + com.ruoyi - ruoyi-common-websocket + ruoyi-common ${ruoyi-vue-plus.version} - com.ruoyi - ruoyi-oss + ruoyi-common-websocket ${ruoyi-vue-plus.version} @@ -340,11 +339,6 @@ ruoyi-demo ${ruoyi-vue-plus.version} - - com.github.gotson - webp-imageio - 0.2.2 - com.github.binarywang weixin-java-miniapp @@ -372,9 +366,9 @@ ruoyi-framework ruoyi-system ruoyi-system-cron + ruoyi-system-file ruoyi-common ruoyi-demo - ruoyi-oss ruoyi-sms pom diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml index 1530cf1..d469040 100644 --- a/ruoyi-admin/pom.xml +++ b/ruoyi-admin/pom.xml @@ -70,7 +70,7 @@ com.ruoyi - ruoyi-oss + ruoyi-system-file diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/UploadController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/UploadController.java index 699d785..2769406 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/UploadController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/UploadController.java @@ -1,56 +1,67 @@ package com.ruoyi.web.controller; -import cn.dev33.satoken.annotation.SaIgnore; import cn.hutool.core.util.ObjectUtil; -import com.ruoyi.common.annotation.Dev; import com.ruoyi.common.annotation.Log; import com.ruoyi.common.core.domain.R; import com.ruoyi.common.enums.BusinessType; import com.ruoyi.common.exception.ServiceException; -import com.ruoyi.system.service.ISysOssService; +import com.ruoyi.file.FileService; import lombok.RequiredArgsConstructor; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.ModelAndView; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.time.Duration; @RequiredArgsConstructor @RestController @RequestMapping("/") public class UploadController { - private final ISysOssService iSysOssService; + private final FileService fileService; - @Log(title = "OSS对象存储", businessType = BusinessType.INSERT) - @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) - public R upload(@RequestPart("file") MultipartFile file, String pre) { - if (ObjectUtil.isNull(file)) { - throw new ServiceException("文件为空"); - } - return R.ok(iSysOssService.upload(file, pre)); - } - @Log(title = "OSS对象存储", businessType = BusinessType.INSERT) - @PostMapping(value = "/uploadImg", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) - public R uploadImg(@RequestPart("file") MultipartFile file, String pre) { - if (ObjectUtil.isNull(file)) { - throw new ServiceException("文件为空"); - } - if(!file.getContentType().startsWith("image/")){ - throw new ServiceException("不是图片"); - } - return R.ok(iSysOssService.uploadImgs(file, pre)); + @Log(title = "OSS对象存储", businessType = BusinessType.INSERT) + @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public R upload(@RequestPart("file") MultipartFile file, String pre) { + if (ObjectUtil.isNull(file)) { + throw new ServiceException("文件为空"); } +// return R.ok(iSysOssService.upload(file, pre)); + return R.ok(fileService.setPrefix(pre).save(file));//TODO: fileService + } - /** - * 下载OSS对象 - * - * @param ossId OSS对象ID - */ - @PostMapping("/download/{ossId}") - public void download(@PathVariable Long ossId, HttpServletResponse response) throws IOException { - iSysOssService.download(ossId, response); + @Log(title = "OSS对象存储", businessType = BusinessType.INSERT) + @PostMapping(value = "/uploadImg", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public R uploadImg(@RequestPart("file") MultipartFile file, String pre) { + if (ObjectUtil.isNull(file)) { + throw new ServiceException("文件为空"); + } + if (!file.getContentType().startsWith("image/")) { + throw new ServiceException("不是图片"); } +// return R.ok(iSysOssService.uploadImgs(file, pre)); + + return R.ok(fileService.setPrefix(pre).setThumbnail().saveImage(file));//TODO: fileService + } + + + /** + * 下载 + * + * @param url + * @param request + * @param response + * @throws IOException + */ + @PostMapping("/download") + public ModelAndView download(String url, HttpServletRequest request, HttpServletResponse response) { +// iSysOssService.download(ossId, response); + fileService.download(url, Duration.ofMinutes(30), request, response);//TODO: fileService + return null; + } } diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssConfigController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssConfigController.java deleted file mode 100644 index 8dc4876..0000000 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssConfigController.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.ruoyi.web.controller.system; - -import cn.dev33.satoken.annotation.SaCheckPermission; -import com.ruoyi.common.annotation.Log; -import com.ruoyi.common.annotation.RepeatSubmit; -import com.ruoyi.common.core.controller.BaseController; -import com.ruoyi.common.core.domain.PageQuery; -import com.ruoyi.common.core.domain.R; -import com.ruoyi.common.core.page.TableDataInfo; -import com.ruoyi.common.core.validate.AddGroup; -import com.ruoyi.common.core.validate.EditGroup; -import com.ruoyi.common.core.validate.QueryGroup; -import com.ruoyi.common.enums.BusinessType; -import com.ruoyi.system.domain.bo.SysOssConfigBo; -import com.ruoyi.system.domain.vo.SysOssConfigVo; -import com.ruoyi.system.service.ISysOssConfigService; -import lombok.RequiredArgsConstructor; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import java.util.Arrays; - -/** - * 对象存储配置 - * - * @author Lion Li - * @author 孤舟烟雨 - * @date 2021-08-13 - */ -@Validated -@RequiredArgsConstructor -@RestController -@RequestMapping("/system/oss/config") -public class SysOssConfigController extends BaseController { - - private final ISysOssConfigService iSysOssConfigService; - - /** - * 查询对象存储配置列表 - */ - @SaCheckPermission("system:oss:list") - @GetMapping("/list") - public TableDataInfo list(@Validated(QueryGroup.class) SysOssConfigBo bo, PageQuery pageQuery) { - return iSysOssConfigService.queryPageList(bo, pageQuery); - } - - /** - * 获取对象存储配置详细信息 - * - * @param ossConfigId OSS配置ID - */ - @SaCheckPermission("system:oss:query") - @GetMapping("/{ossConfigId}") - public R getInfo(@NotNull(message = "主键不能为空") - @PathVariable Long ossConfigId) { - return R.ok(iSysOssConfigService.queryById(ossConfigId)); - } - - /** - * 新增对象存储配置 - */ - @SaCheckPermission("system:oss:add") - @Log(title = "对象存储配置", businessType = BusinessType.INSERT) - @RepeatSubmit() - @PostMapping() - public R add(@Validated(AddGroup.class) @RequestBody SysOssConfigBo bo) { - return toAjax(iSysOssConfigService.insertByBo(bo)); - } - - /** - * 修改对象存储配置 - */ - @SaCheckPermission("system:oss:edit") - @Log(title = "对象存储配置", businessType = BusinessType.UPDATE) - @RepeatSubmit() - @PutMapping() - public R edit(@Validated(EditGroup.class) @RequestBody SysOssConfigBo bo) { - return toAjax(iSysOssConfigService.updateByBo(bo)); - } - - /** - * 删除对象存储配置 - * - * @param ossConfigIds OSS配置ID串 - */ - @SaCheckPermission("system:oss:remove") - @Log(title = "对象存储配置", businessType = BusinessType.DELETE) - @DeleteMapping("/{ossConfigIds}") - public R remove(@NotEmpty(message = "主键不能为空") - @PathVariable Long[] ossConfigIds) { - return toAjax(iSysOssConfigService.deleteWithValidByIds(Arrays.asList(ossConfigIds), true)); - } - - /** - * 状态修改 - */ - @SaCheckPermission("system:oss:edit") - @Log(title = "对象存储状态修改", businessType = BusinessType.UPDATE) - @PutMapping("/changeStatus") - public R changeStatus(@RequestBody SysOssConfigBo bo) { - return toAjax(iSysOssConfigService.updateOssConfigStatus(bo)); - } -} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssController.java deleted file mode 100644 index 606895a..0000000 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssController.java +++ /dev/null @@ -1,117 +0,0 @@ -package com.ruoyi.web.controller.system; - - -import cn.dev33.satoken.annotation.SaCheckPermission; -import cn.hutool.core.convert.Convert; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.http.HttpException; -import cn.hutool.http.HttpUtil; -import com.ruoyi.common.annotation.Log; -import com.ruoyi.common.core.controller.BaseController; -import com.ruoyi.common.core.domain.PageQuery; -import com.ruoyi.common.core.domain.R; -import com.ruoyi.common.core.page.TableDataInfo; -import com.ruoyi.common.core.validate.QueryGroup; -import com.ruoyi.common.enums.BusinessType; -import com.ruoyi.common.exception.ServiceException; -import com.ruoyi.common.utils.file.FileUtils; -import com.ruoyi.oss.core.OssClient; -import com.ruoyi.oss.factory.OssFactory; -import com.ruoyi.system.domain.SysOss; -import com.ruoyi.system.domain.bo.SysOssBo; -import com.ruoyi.system.domain.vo.SysOssVo; -import com.ruoyi.system.service.ISysOssService; -import lombok.RequiredArgsConstructor; -import org.springframework.http.MediaType; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; - -import javax.servlet.http.HttpServletResponse; -import javax.validation.constraints.NotEmpty; -import java.io.IOException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * 文件上传 控制层 - * - * @author Lion Li - */ -@Validated -@RequiredArgsConstructor -@RestController -@RequestMapping("/system/oss") -public class SysOssController extends BaseController { - - private final ISysOssService iSysOssService; - - /** - * 查询OSS对象存储列表 - */ - @SaCheckPermission("system:oss:list") - @GetMapping("/list") - public TableDataInfo list(@Validated(QueryGroup.class) SysOssBo bo, PageQuery pageQuery) { - return iSysOssService.queryPageList(bo, pageQuery); - } - - /** - * 查询OSS对象基于id串 - * - * @param ossIds OSS对象ID串 - */ - @SaCheckPermission("system:oss:list") - @GetMapping("/listByIds/{ossIds}") - public R> listByIds(@NotEmpty(message = "主键不能为空") - @PathVariable Long[] ossIds) { - List list = iSysOssService.listByIds(Arrays.asList(ossIds)); - return R.ok(list); - } - - /** - * 上传OSS对象存储 - * - * @param file 文件 - */ - @SaCheckPermission("system:oss:upload") - @Log(title = "OSS对象存储", businessType = BusinessType.INSERT) - @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) - public R> upload(@RequestPart("file") MultipartFile file) { - if (ObjectUtil.isNull(file)) { - throw new ServiceException("上传文件不能为空"); - } - SysOssVo oss = iSysOssService.upload(file); - Map map = new HashMap<>(2); - map.put("url", oss.getUrl()); - map.put("fileName", oss.getOriginalName()); - map.put("ossId", oss.getOssId().toString()); - return R.ok(map); - } - - /** - * 下载OSS对象 - * - * @param ossId OSS对象ID - */ - @SaCheckPermission("system:oss:download") - @GetMapping("/download/{ossId}") - public void download(@PathVariable Long ossId, HttpServletResponse response) throws IOException { - iSysOssService.download(ossId,response); - } - - /** - * 删除OSS对象存储 - * - * @param ossIds OSS对象ID串 - */ - @SaCheckPermission("system:oss:remove") - @Log(title = "OSS对象存储", businessType = BusinessType.DELETE) - @DeleteMapping("/{ossIds}") - public R remove(@NotEmpty(message = "主键不能为空") - @PathVariable Long[] ossIds) { - return toAjax(iSysOssService.deleteWithValidByIds(Arrays.asList(ossIds), true)); - } - -} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java index 4aa7d5a..a7c0151 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java @@ -5,7 +5,6 @@ import cn.hutool.core.io.FileUtil; import cn.hutool.core.util.StrUtil; import com.ruoyi.common.annotation.Log; import com.ruoyi.common.annotation.RepeatSubmit; -import com.ruoyi.common.constant.UserConstants; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.domain.entity.SysUser; @@ -13,10 +12,7 @@ import com.ruoyi.common.enums.BusinessType; import com.ruoyi.common.helper.LoginHelper; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.file.MimeTypeUtils; -import com.ruoyi.system.domain.SysOss; -import com.ruoyi.system.domain.vo.SysOssVo; -import com.ruoyi.system.service.FileService; -import com.ruoyi.system.service.ISysOssService; +import com.ruoyi.file.FileService; import com.ruoyi.system.service.ISysUserService; import lombok.RequiredArgsConstructor; import org.springframework.http.MediaType; @@ -40,122 +36,125 @@ import java.util.Map; @RequestMapping("/system/user/profile") public class SysProfileController extends BaseController { - private final ISysUserService userService; - private final ISysOssService iSysOssService; - - - /** - * 个人信息 - */ - @GetMapping - public R> profile() { - SysUser user = userService.selectUserById(getUserId()); - Map ajax = new HashMap<>(); - ajax.put("user", user); - ajax.put("roleGroup", userService.selectUserRoleGroup(user.getUserName())); - ajax.put("postGroup", userService.selectUserPostGroup(user.getUserName())); - return R.ok(ajax); + private final ISysUserService userService; + + private final FileService fileService; + + + /** + * 个人信息 + */ + @GetMapping + public R> profile() { + SysUser user = userService.selectUserById(getUserId()); + Map ajax = new HashMap<>(); + ajax.put("user", user); + ajax.put("roleGroup", userService.selectUserRoleGroup(user.getUserName())); + ajax.put("postGroup", userService.selectUserPostGroup(user.getUserName())); + return R.ok(ajax); + } + + /** + * 修改用户 + */ + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PutMapping + public R updateProfile(@RequestBody SysUser user) { + if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) { + return R.fail("手机已存在"); } - - /** - * 修改用户 - */ - @Log(title = "个人信息", businessType = BusinessType.UPDATE) - @PutMapping - public R updateProfile(@RequestBody SysUser user) { - if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) { - return R.fail("手机已存在"); - } - if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) { - return R.fail("邮箱已存在"); - } - user.setUserId(getUserId()); - user.setUserName(null); - user.setPassword(null); - user.setAvatar(null); - user.setDeptId(null); - if (userService.updateUserProfile(user) > 0) { - return R.ok(); - } - return R.fail("修改个人异常"); + if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) { + return R.fail("邮箱已存在"); + } + user.setUserId(getUserId()); + user.setUserName(null); + user.setPassword(null); + user.setAvatar(null); + user.setDeptId(null); + if (userService.updateUserProfile(user) > 0) { + return R.ok(); } - @Log(title = "绑定用户名", businessType = BusinessType.UPDATE) - @RepeatSubmit - @PutMapping("bindUserName") - public R bindUserName(String userName) { - SysUser u = new SysUser(); - u.setUserId(getUserId()); - u.setUserName(userName); - String oldUsername = userService.selectUserById(u.getUserId()).getUserName(); - if(!(StrUtil.isBlank(oldUsername) || oldUsername.startsWith("_"))) { - return R.fail("只能绑定一次"); - } - - if(!userService.checkUserNameUnique(u)){ - return R.fail("账户已存在"); - } - - - if (userService.updateUserProfile(u) > 0) { - return R.ok(); - } - return R.fail("绑定帐号异常"); + return R.fail("修改个人异常"); + } + + @Log(title = "绑定用户名", businessType = BusinessType.UPDATE) + @RepeatSubmit + @PutMapping("bindUserName") + public R bindUserName(String userName) { + SysUser u = new SysUser(); + u.setUserId(getUserId()); + u.setUserName(userName); + String oldUsername = userService.selectUserById(u.getUserId()).getUserName(); + if (!(StrUtil.isBlank(oldUsername) || oldUsername.startsWith("_"))) { + return R.fail("只能绑定一次"); } + if (!userService.checkUserNameUnique(u)) { + return R.fail("账户已存在"); + } - /** - * 重置密码 - * - * @param newPassword 旧密码 - * @param oldPassword 新密码 - */ - @Log(title = "个人信息", businessType = BusinessType.UPDATE) - @PutMapping("/updatePwd") - public R updatePwd(String oldPassword, String newPassword) { - SysUser user = userService.selectUserById(LoginHelper.getUserId()); - String userName = user.getUserName(); - String password = user.getPassword(); - if (StrUtil.isNotBlank(password) && !BCrypt.checkpw(oldPassword, password)) { - return R.fail("旧密码错误"); - } - if (BCrypt.checkpw(newPassword, password)) { - return R.fail("新旧密码相同"); - } - - if (userService.resetUserPwd(userName, BCrypt.hashpw(newPassword)) > 0) { - return R.ok(); - } - return R.fail("修改密码异常"); + if (userService.updateUserProfile(u) > 0) { + return R.ok(); + } + return R.fail("绑定帐号异常"); + } + + + /** + * 重置密码 + * + * @param newPassword 旧密码 + * @param oldPassword 新密码 + */ + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PutMapping("/updatePwd") + public R updatePwd(String oldPassword, String newPassword) { + SysUser user = userService.selectUserById(LoginHelper.getUserId()); + String userName = user.getUserName(); + String password = user.getPassword(); + if (StrUtil.isNotBlank(password) && !BCrypt.checkpw(oldPassword, password)) { + return R.fail("旧密码错误"); + } + if (BCrypt.checkpw(newPassword, password)) { + return R.fail("新旧密码相同"); } - /** - * 头像上传 - * - * @param avatarfile 用户头像 - */ - @Log(title = "用户头像", businessType = BusinessType.UPDATE) - @PostMapping(value = "/avatar", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) - public R> avatar(@RequestPart("avatarfile") MultipartFile avatarfile) throws IOException { - Map ajax = new HashMap<>(); - if (!avatarfile.isEmpty()) { - String extension = FileUtil.extName(avatarfile.getOriginalFilename()); - if(StrUtil.isBlank(extension)){//NOTE: 修复h5上传问题 - extension="png"; - } - if (!StringUtils.equalsAnyIgnoreCase(extension, MimeTypeUtils.IMAGE_EXTENSION)) { - return R.fail("文件格式不正确,请上传" + Arrays.toString(MimeTypeUtils.IMAGE_EXTENSION) + "格式"); - } - - SysOssVo oss = iSysOssService.uploadImgs(avatarfile,"avatar",400,400,null); - String avatar = oss.getUrl(); - - - if (userService.updateUserAvatar(getUsername(), avatar)) { - ajax.put("imgUrl", avatar); - return R.ok(ajax); - } - } - return R.fail("上传图片异常,请联系管理员"); + if (userService.resetUserPwd(userName, BCrypt.hashpw(newPassword)) > 0) { + return R.ok(); + } + return R.fail("修改密码异常"); + } + + /** + * 头像上传 + * + * @param avatarfile 用户头像 + */ + @Log(title = "用户头像", businessType = BusinessType.UPDATE) + @PostMapping(value = "/avatar", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public R> avatar(@RequestPart("avatarfile") MultipartFile avatarfile) throws IOException { + Map ajax = new HashMap<>(); + if (!avatarfile.isEmpty()) { + String extension = FileUtil.extName(avatarfile.getOriginalFilename()); + if (StrUtil.isBlank(extension)) {//NOTE: 修复h5上传问题 + extension = "png"; + } + if (!StringUtils.equalsAnyIgnoreCase(extension, MimeTypeUtils.IMAGE_EXTENSION)) { + return R.fail("文件格式不正确,请上传" + Arrays.toString(MimeTypeUtils.IMAGE_EXTENSION) + "格式"); + } + +// SysOssVo oss = iSysOssService.uploadImgs(avatarfile,"avatar",400,400,null); +// String avatar = oss.getUrl(); + + String avatar = fileService.setSize(400, 400).saveImage(avatarfile);//TODO: fileService + + + if (userService.updateUserAvatar(getUsername(), avatar)) { + ajax.put("imgUrl", avatar); + return R.ok(ajax); + } } + return R.fail("上传图片异常,请联系管理员"); + } } diff --git a/ruoyi-admin/src/main/resources/application-dev.yml b/ruoyi-admin/src/main/resources/application-dev.yml index 2132d60..34bb80b 100644 --- a/ruoyi-admin/src/main/resources/application-dev.yml +++ b/ruoyi-admin/src/main/resources/application-dev.yml @@ -3,12 +3,43 @@ ruoyi: # 是否是开发模式 dev: true - # 本地文件存储配置 - upload: - # 资源访问前缀 - pre: /upload - # 物理保存地址 - save-path: /.data/upload + + # 文件存储配置 + file: + max-width: 1500 + max-height: 1500 + th-width: 200 + th-height: 200 + watermark: classpath:/watermark.png + default-platform: minio #默认使用的存储平台 + local-plus: + - platform: local # 存储平台标识 + enable-storage: true #启用存储 + enable-access: true #启用访问(线上请使用 Nginx 配置,效率更高) + path-patterns: /upload/** # 访问路径 + storage-path: /upload/ # 存储路径 + domain: "/upload/" # 访问域名,例如:“http://127.0.0.1:8030/file/”,注意后面要和 path-patterns 保持一致,“/”结尾,本地存储建议使用相对路径,方便后期更换域名 + base-path: "" # 基础路径 + minio: + - platform: minio + enable-storage: true # 启用存储 + access-key: ${ruoyi.name} + secret-key: ${ruoyi.name}1415926 + end-point: http://192.168.3.222:9000 + bucket-name: files + domain: "/files/" # 访问域名,注意“/”结尾,例如:http://minio.abc.com/abc/ + base-path: "" # 基础路径 + aliyun-oss: + - platform: aliyun # 存储平台标识 + enable-storage: true # 启用存储 + access-key: LTAI5tKkFMwc4SuDF8LpgRQ3 + secret-key: 74S18FfuyxTd85iYKifsVXjY5DhVAB + end-point: https://oss-cn-shenzhen.aliyuncs.com + bucket-name: base-2024 + domain: "https://base-2024.oss-cn-shenzhen.aliyuncs.com/" # 访问域名,注意“/”结尾,例如:https://abc.oss-cn-shanghai.aliyuncs.com/ + base-path: "" # 基础路径 + + logging: level: diff --git a/ruoyi-admin/src/main/resources/application-local.yml.template b/ruoyi-admin/src/main/resources/application-local.yml.template index 2132d60..34bb80b 100644 --- a/ruoyi-admin/src/main/resources/application-local.yml.template +++ b/ruoyi-admin/src/main/resources/application-local.yml.template @@ -3,12 +3,43 @@ ruoyi: # 是否是开发模式 dev: true - # 本地文件存储配置 - upload: - # 资源访问前缀 - pre: /upload - # 物理保存地址 - save-path: /.data/upload + + # 文件存储配置 + file: + max-width: 1500 + max-height: 1500 + th-width: 200 + th-height: 200 + watermark: classpath:/watermark.png + default-platform: minio #默认使用的存储平台 + local-plus: + - platform: local # 存储平台标识 + enable-storage: true #启用存储 + enable-access: true #启用访问(线上请使用 Nginx 配置,效率更高) + path-patterns: /upload/** # 访问路径 + storage-path: /upload/ # 存储路径 + domain: "/upload/" # 访问域名,例如:“http://127.0.0.1:8030/file/”,注意后面要和 path-patterns 保持一致,“/”结尾,本地存储建议使用相对路径,方便后期更换域名 + base-path: "" # 基础路径 + minio: + - platform: minio + enable-storage: true # 启用存储 + access-key: ${ruoyi.name} + secret-key: ${ruoyi.name}1415926 + end-point: http://192.168.3.222:9000 + bucket-name: files + domain: "/files/" # 访问域名,注意“/”结尾,例如:http://minio.abc.com/abc/ + base-path: "" # 基础路径 + aliyun-oss: + - platform: aliyun # 存储平台标识 + enable-storage: true # 启用存储 + access-key: LTAI5tKkFMwc4SuDF8LpgRQ3 + secret-key: 74S18FfuyxTd85iYKifsVXjY5DhVAB + end-point: https://oss-cn-shenzhen.aliyuncs.com + bucket-name: base-2024 + domain: "https://base-2024.oss-cn-shenzhen.aliyuncs.com/" # 访问域名,注意“/”结尾,例如:https://abc.oss-cn-shanghai.aliyuncs.com/ + base-path: "" # 基础路径 + + logging: level: diff --git a/ruoyi-admin/src/main/resources/application-prod.yml b/ruoyi-admin/src/main/resources/application-prod.yml index b766f71..a40cc6e 100644 --- a/ruoyi-admin/src/main/resources/application-prod.yml +++ b/ruoyi-admin/src/main/resources/application-prod.yml @@ -3,12 +3,42 @@ ruoyi: # 是否是开发模式 dev: false - # 本地文件存储配置 - upload: - # 资源访问前缀 - pre: /upload - # 物理保存地址 - save-path: /server/upload + + + # 文件存储配置 + file: + max-width: 1500 + max-height: 1500 + th-width: 200 + th-height: 200 + watermark: classpath:/watermark.png + default-platform: minio #默认使用的存储平台 + local-plus: + - platform: local # 存储平台标识 + enable-storage: true #启用存储 + enable-access: true #启用访问(线上请使用 Nginx 配置,效率更高) + path-patterns: /upload/** # 访问路径 + storage-path: /server/upload/ # 存储路径 + domain: "/upload/" # 访问域名,例如:“http://127.0.0.1:8030/file/”,注意后面要和 path-patterns 保持一致,“/”结尾,本地存储建议使用相对路径,方便后期更换域名 + base-path: "" # 基础路径 + minio: + - platform: minio + enable-storage: true # 启用存储 + access-key: ${ruoyi.name} + secret-key: ${ruoyi.name}1415926 + end-point: http://minio:9000 + bucket-name: files + domain: "/files/" # 访问域名,注意“/”结尾,例如:http://minio.abc.com/abc/ + base-path: "" # 基础路径 + aliyun-oss: + - platform: aliyun # 存储平台标识 + enable-storage: true # 启用存储 + access-key: XXXXXXXXXXXXXXXXXXXXXX + secret-key: XXXXXXXXXXXXXXXXXXXXXXXXXXXX + end-point: https://oss-cn-shenzhen.aliyuncs.com + bucket-name: base2024 + domain: "https://base2024.oss-cn-shenzhen.aliyuncs.com/" # 访问域名,注意“/”结尾,例如:https://abc.oss-cn-shanghai.aliyuncs.com/ + base-path: "" # 基础路径 --- # 临时文件存储位置 避免临时文件被系统清理报错 diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index f778ffb..cc644c7 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -14,12 +14,6 @@ ruoyi: addressEnabled: true # 缓存懒加载 cacheLazy: false - # 本地文件存储配置 - upload: - # 资源访问前缀 - pre: /upload - # 物理保存地址 - save-path: /upload # 小程序的用户默认设置 default-user: # 所在单位 @@ -34,6 +28,9 @@ ruoyi: # 岗位组 post-ids: - 4 + - + + --- # 验证码配置 diff --git a/ruoyi-admin/src/test/java/com/ruoyi/test/PasswordTest.java b/ruoyi-admin/src/test/java/com/ruoyi/test/PasswordTest.java index def913f..6495f20 100644 --- a/ruoyi-admin/src/test/java/com/ruoyi/test/PasswordTest.java +++ b/ruoyi-admin/src/test/java/com/ruoyi/test/PasswordTest.java @@ -1,9 +1,12 @@ package com.ruoyi.test; import cn.dev33.satoken.secure.BCrypt; +import cn.hutool.core.util.URLUtil; +import lombok.SneakyThrows; import org.junit.jupiter.api.Test; import java.io.File; +import java.net.URL; public class PasswordTest { @@ -16,4 +19,10 @@ public class PasswordTest { public void abc() { new File("E:\\upload\\2023\\7\\12\\75s0.jpg").delete(); } + + @Test + public void urlEncode() { + System.out.println(URLUtil.encode("/files/default/2024/10/24/553vijcx66we/长沙销售部.txt")); + } + } diff --git a/ruoyi-admin/src/test/java/com/ruoyi/test/XFileStorageTest.java b/ruoyi-admin/src/test/java/com/ruoyi/test/XFileStorageTest.java new file mode 100644 index 0000000..9360921 --- /dev/null +++ b/ruoyi-admin/src/test/java/com/ruoyi/test/XFileStorageTest.java @@ -0,0 +1,201 @@ +package com.ruoyi.test; + +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.HexUtil; +import com.ruoyi.TestSuper; +import com.ruoyi.file.FileService; +import lombok.NoArgsConstructor; +import lombok.SneakyThrows; +import org.dromara.x.file.storage.core.FileInfo; +import org.dromara.x.file.storage.core.FileStorageService; +import org.dromara.x.file.storage.core.tika.TikaFactory; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.time.Duration; +import java.time.LocalDate; + +@NoArgsConstructor +public class XFileStorageTest extends TestSuper { + + @Autowired + private FileStorageService fileStorageService;//注入实列 + + @Autowired + private FileService fileService; + + @Autowired + private TikaFactory tikaFactory; + + + public InputStream getInstream() { + return this.getClass().getResourceAsStream("/test.png"); + } + + @Test + @DisplayName("分片上传") + @Disabled + @SneakyThrows + public void testMultipartUpload() { + File file = new File("D:\\test.mp4"); + String uploadId = fileService.setPlatform("aliyun").setUri("/test.mp4").multipartUploadInit(); + try ( + InputStream in = new FileInputStream(file); + ) { + byte[] bs = new byte[5 * 1024 * 1024];//每一片5MB + int len = 0; + int partNumber = 0; + try { + while ((len = in.read(bs)) > 0) { + partNumber++; + ByteArrayInputStream bin = new ByteArrayInputStream(bs, 0, len); + fileService.multipartUpload(uploadId, partNumber, bin); + out("上传分片成功:" + partNumber + " len:" + len); + + } + out("分片上传成功:" + fileService.multipartUploadComplete(uploadId)); + } catch (Exception e) { + out("分片上传失败", e); + fileService.multipartUploadAbort(uploadId); + } + + } + } + + + @Test + @DisplayName("分片上传") + @Disabled + @SneakyThrows + public void testMultipartUpload1() { + File file = new File("D:\\test.mp4");//文件大概6M +// File file = new File("D:\\VMware-images\\CentOS-7-x86_64-Minimal-2009.iso");;//900M+ + FileInfo fileInfo = fileStorageService.initiateMultipartUpload().setPlatform("aliyun").setPath("default/").setSaveFilename(file.getName()).init(); + try ( + InputStream in = new FileInputStream(file); + ) { + byte[] bs = new byte[5 * 1024 * 1024];//每一片5MB + int len = 0; + int partNumber = 1; + try { + while ((len = in.read(bs)) > 0) { + partNumber++; + ByteArrayInputStream bin = new ByteArrayInputStream(bs, 0, len); + fileStorageService.uploadPart(fileInfo, partNumber, bin).upload(); + out("上传分片成功:" + partNumber + " len:" + len); + + } + out("分片上传成功:" + fileStorageService.completeMultipartUpload(fileInfo).complete().getUrl()); + } catch (Exception e) { + out("分片上传失败", e); + fileStorageService.abortMultipartUpload(fileInfo).abort(); + } + } + } + + + @Test + @Disabled + @DisplayName("读取配置信息") + public void testConfig() throws Exception { + out(fileStorageService.getProperties()); + } + + @Test + @Disabled + @DisplayName("保存文件") + @SneakyThrows + public void saveTest() { +// out("保存随机文件名:" + fileService.setFilename("test.png").save(getInstream())); +// out("保存文件名:" + fileService.setKeepFilename().setFilename("test.png").save(getInstream())); +// out("指定保存规则:" + fileService.setFilename("test.png").setRule("/{yyyy}/{MM}/{dd}/{id}.{ext}").save(getInstream())); + out("指定保存路径:" + fileService.setUri("/aa/bb/cc/a.png").save(getInstream())); + out("aliyun指定保存路径:" + fileService.setPlatform("aliyun").setUri("/aa/bb/cc/a.png").save(getInstream())); + out("local指定保存路径:" + fileService.setPlatform("local").setUri("/aa/bb/cc/a.png").save(getInstream())); +// out("指定路径前缀保存随机文件名:" + fileService.setPrefix("test").setFilename("test.png").save(getInstream())); +// out("指定平台保存随机文件名:" + fileService.setPlatform("local").setFilename("test.png").save(getInstream())); + + } + + @Test + @Disabled + @DisplayName("保存图片") + @SneakyThrows + public void saveImage() { +// out("保存图片:" + fileService.saveImage(getInstream())); +// out("保存图片+缩略图:" + fileService.setThumbnail().saveImage(getInstream())); +// out("保存图片+调整大小+缩略图:" + fileService.setSize(500,500).setThumbnail().saveImage(getInstream())); +// out("保存图片+水印+调整大小+缩略图:" + fileService.setPlatform("aliyun").setWatermark().setSize(700, 700).setThumbnail().saveImage(getInstream())); + out("保存图片+水印+调整大小+缩略图+源文件:" + fileService.setPlatform("local").setFilename("test.png").setSaveSrc().setWatermark().setSize(700, 700).setThumbnail().saveImage(getInstream())); + } + + + private String uri = "/files/default/aa/bb/cc/a.png"; + + private String aliyun = "https://base-2024.oss-cn-shenzhen.aliyuncs.com/default/aa/bb/cc/a.png"; + + + @Test + @Disabled + @DisplayName("删除文件") + @SneakyThrows + public void deleteTest() { +// fileService.delete(uri); +// fileService.setPlatform("local").delete("/upload/default/aa/bb/cc/a.png"); + fileService.setPlatform("aliyun").delete(aliyun); + } + + @Test + @Disabled + @DisplayName("下载文件") + @SneakyThrows + public void downloadTest() { +// FileInfo fileInfo = new FileInfo(); +// fileInfo.setPath("default/aa/bb/cc/"); +// fileInfo.setFilename("a.png"); +// fileInfo.setPlatform(fileStorageService.getFileStorage("aliyun").getPlatform()); +// out(HexUtil.encodeHex(fileStorageService.download(fileInfo).bytes())); +// out(HexUtil.encodeHex(fileService.download(uri).bytes())); + out(HexUtil.encodeHex(fileService.setPlatform("aliyun").download(aliyun).bytes())); + } + + @Test + @Disabled + @DisplayName("生成预签名URL") + @SneakyThrows + public void generatePresignedUrlTest() { + out(fileService.generatePresignedUrl(uri, Duration.ofMinutes(30))); + out(fileService.setPlatform("aliyun").generatePresignedUrl(aliyun, Duration.ofMinutes(30))); + try { + out(fileService.setPlatform("local").generatePresignedUrl(uri, Duration.ofMinutes(30))); + } catch (Exception e) { + out("不支持预签名", e); + } + } + + @Test + @Disabled + @DisplayName("类型检测") + @SneakyThrows + public void contentTypeTest() { + System.out.println(tikaFactory.getTika().detect(getInstream())); + } + + @Test + @Disabled + @DisplayName("转换格式") + @SneakyThrows + public void testWebp() { + BufferedImage image = ImageIO.read(getInstream()); + ImageIO.write(image, "webp", new File("/test.webp")); + } +} diff --git a/ruoyi-admin/src/test/resources/test.png b/ruoyi-admin/src/test/resources/test.png new file mode 100644 index 0000000..32e6b0c Binary files /dev/null and b/ruoyi-admin/src/test/resources/test.png differ diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java b/ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java index bbd984c..495546b 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java @@ -1,6 +1,5 @@ package com.ruoyi.common.config; -import com.baomidou.mybatisplus.annotation.TableField; import com.ruoyi.common.enums.UserStatus; import com.ruoyi.common.enums.UserType; import lombok.Data; @@ -16,97 +15,68 @@ import org.springframework.stereotype.Component; @Data @Component -@ConfigurationProperties(prefix = "ruoyi") +@ConfigurationProperties(prefix = "ruoyi", ignoreInvalidFields = true) public class RuoYiConfig { + /** + * 开发模式 + */ + private Boolean dev = false; + /** + * 项目名称 + */ + private String name; + + /** + * 版本 + */ + private String version; + + /** + * 版权年份 + */ + private String copyrightYear; + + /** + * 实例演示开关 + */ + private boolean demoEnabled; + + /** + * 缓存懒加载 + */ + private boolean cacheLazy; + + /** + * 获取地址开关 + */ + @Getter + private static boolean addressEnabled; + + public void setAddressEnabled(boolean addressEnabled) { + RuoYiConfig.addressEnabled = addressEnabled; + } + + private DefaultUser defaultUser = new DefaultUser(); + + @Data + public static class DefaultUser { + private Long deptId = 100L; + + private String userType = UserType.APP_USER.getUserType(); /** - * 开发模式 + * 帐号状态(0正常 1停用) */ - private Boolean dev=false; + private String status = UserStatus.OK.getCode(); /** - * 项目名称 + * 角色组 */ - private String name; + private Long[] roleIds = {2L}; /** - * 版本 + * 岗位组 */ - private String version; + private Long[] postIds = {4L}; - /** - * 版权年份 - */ - private String copyrightYear; - - /** - * 实例演示开关 - */ - private boolean demoEnabled; - - /** - * 缓存懒加载 - */ - private boolean cacheLazy; - - /** - * 获取地址开关 - */ - @Getter - private static boolean addressEnabled; - - public void setAddressEnabled(boolean addressEnabled) { - RuoYiConfig.addressEnabled = addressEnabled; - } - - private DefaultUser defaultUser = new DefaultUser(); - - private Tencentcloud tencentcloud = new Tencentcloud(); - - @Data - public static class DefaultUser { - private Long deptId = 100L; - - private String userType = UserType.APP_USER.getUserType(); - /** - * 帐号状态(0正常 1停用) - */ - private String status = UserStatus.OK.getCode(); - /** - * 角色组 - */ - private Long[] roleIds = {2L}; - - /** - * 岗位组 - */ - private Long[] postIds = {4L}; - - } - - - public Upload upload = new Upload(); - - /** - * 本地文件存储配置 - */ - @Data - public static class Upload { - - /** - * 资源地址前缀 - */ - public String pre = "/upload"; - - /** - * 保存位置 - */ - public String savePath="/upload"; - } + } - @Data - public static class Tencentcloud { - private String secretId = "AKIDoeWFoKdhaLuFLD1sX2LRItFMI2f7NRRh"; - private String secretKey = "RkspdHuOflngNgnhXRL4Zpq096pLhrmQ"; - private String ocrEndpoint = "ocr.ap-guangzhou.tencentcloudapi.com"; - private String ocrRegion = "ap-guangzhou"; - } } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/OssUrlTranslationImpl.java b/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/OssUrlTranslationImpl.java deleted file mode 100644 index 69ebd9a..0000000 --- a/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/OssUrlTranslationImpl.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.ruoyi.common.translation.impl; - -import com.ruoyi.common.annotation.TranslationType; -import com.ruoyi.common.constant.TransConstant; -import com.ruoyi.common.core.service.OssService; -import com.ruoyi.common.translation.TranslationInterface; -import lombok.AllArgsConstructor; -import org.springframework.stereotype.Component; - -/** - * OSS翻译实现 - * - * @author Lion Li - */ -@Component -@AllArgsConstructor -@TranslationType(type = TransConstant.OSS_ID_TO_URL) -public class OssUrlTranslationImpl implements TranslationInterface { - - private final OssService ossService; - - public String translation(Object key, String other) { - return ossService.selectUrlByIds(key.toString()); - } -} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java index 1ef2229..fda2462 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java @@ -1,6 +1,8 @@ package com.ruoyi.common.utils.file; import cn.hutool.core.io.FileUtil; +import cn.hutool.core.lang.UUID; +import com.ruoyi.common.utils.redis.RedisUtils; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -8,6 +10,7 @@ import javax.servlet.http.HttpServletResponse; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import java.time.Duration; /** * 文件处理工具类 @@ -17,36 +20,74 @@ import java.nio.charset.StandardCharsets; @NoArgsConstructor(access = AccessLevel.PRIVATE) public class FileUtils extends FileUtil { - /** - * 下载文件名重新编码 - * - * @param response 响应对象 - * @param realFileName 真实文件名 - */ - public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException { - String percentEncodedFileName = percentEncode(realFileName); - - StringBuilder contentDispositionValue = new StringBuilder(); - contentDispositionValue.append("attachment; filename=") - .append(percentEncodedFileName) - .append(";") - .append("filename*=") - .append("utf-8''") - .append(percentEncodedFileName); - - response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename"); - response.setHeader("Content-disposition", contentDispositionValue.toString()); - response.setHeader("download-filename", percentEncodedFileName); - } - - /** - * 百分号编码工具方法 - * - * @param s 需要百分号编码的字符串 - * @return 百分号编码后的字符串 - */ - public static String percentEncode(String s) throws UnsupportedEncodingException { - String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString()); - return encode.replaceAll("\\+", "%20"); - } + /** + * 下载文件名重新编码 + * + * @param response 响应对象 + * @param realFileName 真实文件名 + */ + public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException { + String percentEncodedFileName = percentEncode(realFileName); + + StringBuilder contentDispositionValue = new StringBuilder(); + contentDispositionValue.append("attachment; filename=") + .append(percentEncodedFileName) + .append(";") + .append("filename*=") + .append("utf-8''") + .append(percentEncodedFileName); + + response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename"); + response.setHeader("Content-disposition", contentDispositionValue.toString()); + response.setHeader("download-filename", percentEncodedFileName); + } + + /** + * 百分号编码工具方法 + * + * @param s 需要百分号编码的字符串 + * @return 百分号编码后的字符串 + */ + public static String percentEncode(String s) throws UnsupportedEncodingException { + String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString()); + return encode.replaceAll("\\+", "%20"); + } + + public static final String UPLOAD_KEY_PREFIX = "UPLOAD:KEY:"; + + /** + * 获取上传凭证 + * @param duration 有效期 + * @return + */ + public static String getUploadKey(Duration duration) { + String key = UUID.fastUUID().toString(true); + RedisUtils.setCacheObject(UPLOAD_KEY_PREFIX + key, true, duration); + return key; + } + + /** + * 获取上传凭证,有效期30分钟 + * @return + */ + public static String getUploadKey() { + return getUploadKey(Duration.ofMinutes(60)); + } + + /** + * 检查上传凭证是否存在 + * @param key + * @return + */ + public static boolean exitisUploadKey(String key){ + return RedisUtils.isExistsObject(UPLOAD_KEY_PREFIX + key); + } + + /** + * 删除上传凭证 + * @param key + */ + public static void removeUploadKey(String key){ + RedisUtils.deleteObject(UPLOAD_KEY_PREFIX + key); + } } diff --git a/ruoyi-oss/pom.xml b/ruoyi-oss/pom.xml deleted file mode 100644 index c2e1e99..0000000 --- a/ruoyi-oss/pom.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - ruoyi-vue-plus - com.ruoyi - 4.6.0 - - 4.0.0 - - ruoyi-oss - - - OSS对象存储模块 - - - - - - - com.ruoyi - ruoyi-common - - - - com.amazonaws - aws-java-sdk-s3 - - - - - diff --git a/ruoyi-oss/src/main/java/com/ruoyi/oss/constant/OssConstant.java b/ruoyi-oss/src/main/java/com/ruoyi/oss/constant/OssConstant.java deleted file mode 100644 index 06202d0..0000000 --- a/ruoyi-oss/src/main/java/com/ruoyi/oss/constant/OssConstant.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.ruoyi.oss.constant; - -import java.util.Arrays; -import java.util.List; - -/** - * 对象存储常量 - * - * @author Lion Li - */ -public interface OssConstant { - - /** - * 默认配置KEY - */ - String DEFAULT_CONFIG_KEY = "sys_oss:default_config"; - - /** - * 预览列表资源开关Key - */ - String PEREVIEW_LIST_RESOURCE_KEY = "sys.oss.previewListResource"; - - /** - * 系统数据ids - */ - List SYSTEM_DATA_IDS = Arrays.asList(1L, 2L, 3L, 4L); - - /** - * 云服务商 - */ - String[] CLOUD_SERVICE = new String[] {"aliyun", "qcloud", "qiniu", "obs"}; - - /** - * https 状态 - */ - String IS_HTTPS = "Y"; - -} diff --git a/ruoyi-oss/src/main/java/com/ruoyi/oss/core/OssClient.java b/ruoyi-oss/src/main/java/com/ruoyi/oss/core/OssClient.java deleted file mode 100644 index 76258be..0000000 --- a/ruoyi-oss/src/main/java/com/ruoyi/oss/core/OssClient.java +++ /dev/null @@ -1,268 +0,0 @@ -package com.ruoyi.oss.core; - -import cn.hutool.core.io.IoUtil; -import cn.hutool.core.util.IdUtil; -import com.amazonaws.ClientConfiguration; -import com.amazonaws.HttpMethod; -import com.amazonaws.Protocol; -import com.amazonaws.auth.AWSCredentials; -import com.amazonaws.auth.AWSCredentialsProvider; -import com.amazonaws.auth.AWSStaticCredentialsProvider; -import com.amazonaws.auth.BasicAWSCredentials; -import com.amazonaws.client.builder.AwsClientBuilder; -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.AmazonS3Client; -import com.amazonaws.services.s3.AmazonS3ClientBuilder; -import com.amazonaws.services.s3.model.*; -import com.ruoyi.common.utils.DateUtils; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.oss.constant.OssConstant; -import com.ruoyi.oss.entity.UploadResult; -import com.ruoyi.oss.enumd.AccessPolicyType; -import com.ruoyi.oss.enumd.PolicyType; -import com.ruoyi.oss.exception.OssException; -import com.ruoyi.oss.properties.OssProperties; -import lombok.Getter; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.net.URL; -import java.util.Date; - -/** - * S3 存储协议 所有兼容S3协议的云厂商均支持 - * 阿里云 腾讯云 七牛云 minio - * - * @author Lion Li - */ -public class OssClient { - - private final String configKey; - - @Getter - private final OssProperties properties; - - private final AmazonS3 client; - - public OssClient(String configKey, OssProperties ossProperties) { - this.configKey = configKey; - this.properties = ossProperties; - try { - AwsClientBuilder.EndpointConfiguration endpointConfig = - new AwsClientBuilder.EndpointConfiguration(properties.getEndpoint(), properties.getRegion()); - - AWSCredentials credentials = new BasicAWSCredentials(properties.getAccessKey(), properties.getSecretKey()); - AWSCredentialsProvider credentialsProvider = new AWSStaticCredentialsProvider(credentials); - ClientConfiguration clientConfig = new ClientConfiguration(); - if (OssConstant.IS_HTTPS.equals(properties.getIsHttps())) { - clientConfig.setProtocol(Protocol.HTTPS); - } else { - clientConfig.setProtocol(Protocol.HTTP); - } - AmazonS3ClientBuilder build = AmazonS3Client.builder() - .withEndpointConfiguration(endpointConfig) - .withClientConfiguration(clientConfig) - .withCredentials(credentialsProvider) - .disableChunkedEncoding(); - if (!StringUtils.containsAny(properties.getEndpoint(), OssConstant.CLOUD_SERVICE)) { - // minio 使用https限制使用域名访问 需要此配置 站点填域名 - build.enablePathStyleAccess(); - } - this.client = build.build(); - - createBucket(); - } catch (Exception e) { - if (e instanceof OssException) { - throw e; - } - throw new OssException("配置错误! 请检查系统配置:[" + e.getMessage() + "]"); - } - } - - public void createBucket() { - try { - String bucketName = properties.getBucketName(); - if (client.doesBucketExistV2(bucketName)) { - return; - } - CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName); - AccessPolicyType accessPolicy = getAccessPolicy(); - createBucketRequest.setCannedAcl(accessPolicy.getAcl()); - client.createBucket(createBucketRequest); - client.setBucketPolicy(bucketName, getPolicy(bucketName, accessPolicy.getPolicyType())); - } catch (Exception e) { - throw new OssException("创建Bucket失败, 请核对配置信息:[" + e.getMessage() + "]"); - } - } - - public UploadResult upload(byte[] data, String path, String contentType) { - return upload(new ByteArrayInputStream(data), path, contentType); - } - - public UploadResult upload(InputStream inputStream, String path, String contentType) { - if (!(inputStream instanceof ByteArrayInputStream)) { - inputStream = new ByteArrayInputStream(IoUtil.readBytes(inputStream)); - } - try { - ObjectMetadata metadata = new ObjectMetadata(); - metadata.setContentType(contentType); - metadata.setContentLength(inputStream.available()); - PutObjectRequest putObjectRequest = new PutObjectRequest(properties.getBucketName(), path, inputStream, metadata); - // 设置上传对象的 Acl 为公共读 - putObjectRequest.setCannedAcl(getAccessPolicy().getAcl()); - client.putObject(putObjectRequest); - } catch (Exception e) { - throw new OssException("上传文件失败,请检查配置信息:[" + e.getMessage() + "]"); - } - return UploadResult.builder().url(getUrl() + "/" + path).filename(path).build(); - } - - public void delete(String path) { - path = path.replace(getUrl() + "/", ""); - try { - client.deleteObject(properties.getBucketName(), path); - } catch (Exception e) { - throw new OssException("删除文件失败,请检查配置信息:[" + e.getMessage() + "]"); - } - } - - public UploadResult uploadSuffix(byte[] data, String suffix, String contentType) { - return upload(data, getPath(properties.getPrefix(), suffix), contentType); - } - - public UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType) { - return upload(inputStream, getPath(properties.getPrefix(), suffix), contentType); - } - - /** - * 获取文件元数据 - * - * @param path 完整文件路径 - */ - public ObjectMetadata getObjectMetadata(String path) { - path = path.replace(getUrl() + "/", ""); - S3Object object = client.getObject(properties.getBucketName(), path); - return object.getObjectMetadata(); - } - - public InputStream getObjectContent(String path) { - path = path.replace(getUrl() + "/", ""); - S3Object object = client.getObject(properties.getBucketName(), path); - return object.getObjectContent(); - } - - public String getUrl() { - String domain = properties.getDomain(); - String endpoint = properties.getEndpoint(); - String header = OssConstant.IS_HTTPS.equals(properties.getIsHttps()) ? "https://" : "http://"; - // 云服务商直接返回 - if (StringUtils.containsAny(endpoint, OssConstant.CLOUD_SERVICE)) { - if (StringUtils.isNotBlank(domain)) { - if(domain.contains("{root}")){ - return domain.replace("{root}", ""); - }else if(domain.contains("//")){ - return domain; - }else{ - return header + domain; - } - - } - return header + properties.getBucketName() + "." + endpoint; - } - // minio 单独处理 - if (StringUtils.isNotBlank(domain)) { - if(domain.equalsIgnoreCase("{root}")){ - return domain.replace("{root}", "")+ "/" + properties.getBucketName(); - }else if(domain.contains("//")){ - return domain + "/" + properties.getBucketName(); - }else{ - return header + domain + "/" + properties.getBucketName(); - } - } - return header + endpoint + "/" + properties.getBucketName(); - } - - public String getPath(String prefix, String suffix) { - // 生成uuid - String uuid = IdUtil.fastSimpleUUID(); - // 文件路径 - String path = DateUtils.datePath() + "/" + uuid; - if (StringUtils.isNotBlank(prefix)) { - path = prefix + "/" + path; - } - return path + suffix; - } - - - public String getConfigKey() { - return configKey; - } - - /** - * 获取私有URL链接 - * - * @param objectKey 对象KEY - * @param second 授权时间 - */ - public String getPrivateUrl(String objectKey, Integer second) { - GeneratePresignedUrlRequest generatePresignedUrlRequest = - new GeneratePresignedUrlRequest(properties.getBucketName(), objectKey) - .withMethod(HttpMethod.GET) - .withExpiration(new Date(System.currentTimeMillis() + 1000L * second)); - URL url = client.generatePresignedUrl(generatePresignedUrlRequest); - return url.toString(); - } - - /** - * 检查配置是否相同 - */ - public boolean checkPropertiesSame(OssProperties properties) { - return this.properties.equals(properties); - } - - /** - * 获取当前桶权限类型 - * - * @return 当前桶权限类型code - */ - public AccessPolicyType getAccessPolicy() { - return AccessPolicyType.getByType(properties.getAccessPolicy()); - } - - private static String getPolicy(String bucketName, PolicyType policyType) { - StringBuilder builder = new StringBuilder(); - builder.append("{\n\"Statement\": [\n{\n\"Action\": [\n"); - if (policyType == PolicyType.WRITE) { - builder.append("\"s3:GetBucketLocation\",\n\"s3:ListBucketMultipartUploads\"\n"); - } else if (policyType == PolicyType.READ_WRITE) { - builder.append("\"s3:GetBucketLocation\",\n\"s3:ListBucket\",\n\"s3:ListBucketMultipartUploads\"\n"); - } else { - builder.append("\"s3:GetBucketLocation\"\n"); - } - builder.append("],\n\"Effect\": \"Allow\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::"); - builder.append(bucketName); - builder.append("\"\n},\n"); - if (policyType == PolicyType.READ) { - builder.append("{\n\"Action\": [\n\"s3:ListBucket\"\n],\n\"Effect\": \"Deny\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::"); - builder.append(bucketName); - builder.append("\"\n},\n"); - } - builder.append("{\n\"Action\": "); - switch (policyType) { - case WRITE: - builder.append("[\n\"s3:AbortMultipartUpload\",\n\"s3:DeleteObject\",\n\"s3:ListMultipartUploadParts\",\n\"s3:PutObject\"\n],\n"); - break; - case READ_WRITE: - builder.append("[\n\"s3:AbortMultipartUpload\",\n\"s3:DeleteObject\",\n\"s3:GetObject\",\n\"s3:ListMultipartUploadParts\",\n\"s3:PutObject\"\n],\n"); - break; - default: - builder.append("\"s3:GetObject\",\n"); - break; - } - builder.append("\"Effect\": \"Allow\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::"); - builder.append(bucketName); - builder.append("/*\"\n}\n],\n\"Version\": \"2012-10-17\"\n}\n"); - return builder.toString(); - } - -} diff --git a/ruoyi-oss/src/main/java/com/ruoyi/oss/entity/UploadResult.java b/ruoyi-oss/src/main/java/com/ruoyi/oss/entity/UploadResult.java deleted file mode 100644 index 379d283..0000000 --- a/ruoyi-oss/src/main/java/com/ruoyi/oss/entity/UploadResult.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.ruoyi.oss.entity; - -import lombok.Builder; -import lombok.Data; - -/** - * 上传返回体 - * - * @author Lion Li - */ -@Data -@Builder -public class UploadResult { - - /** - * 文件路径 - */ - private String url; - - /** - * 文件名 - */ - private String filename; -} diff --git a/ruoyi-oss/src/main/java/com/ruoyi/oss/enumd/AccessPolicyType.java b/ruoyi-oss/src/main/java/com/ruoyi/oss/enumd/AccessPolicyType.java deleted file mode 100644 index 1cae670..0000000 --- a/ruoyi-oss/src/main/java/com/ruoyi/oss/enumd/AccessPolicyType.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.ruoyi.oss.enumd; - -import com.amazonaws.services.s3.model.CannedAccessControlList; -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * 桶访问策略配置 - * - * @author 陈賝 - */ -@Getter -@AllArgsConstructor -public enum AccessPolicyType { - - /** - * private - */ - PRIVATE("0", CannedAccessControlList.Private, PolicyType.WRITE), - - /** - * public - */ - PUBLIC("1", CannedAccessControlList.PublicRead, PolicyType.READ), - - /** - * custom - */ - CUSTOM("2",CannedAccessControlList.PublicRead, PolicyType.READ); - - /** - * 桶 权限类型 - */ - private final String type; - - /** - * 文件对象 权限类型 - */ - private final CannedAccessControlList acl; - - /** - * 桶策略类型 - */ - private final PolicyType policyType; - - public static AccessPolicyType getByType(String type) { - for (AccessPolicyType value : values()) { - if (value.getType().equals(type)) { - return value; - } - } - throw new RuntimeException("'type' not found By " + type); - } - -} diff --git a/ruoyi-oss/src/main/java/com/ruoyi/oss/enumd/PolicyType.java b/ruoyi-oss/src/main/java/com/ruoyi/oss/enumd/PolicyType.java deleted file mode 100644 index 606f0f4..0000000 --- a/ruoyi-oss/src/main/java/com/ruoyi/oss/enumd/PolicyType.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.ruoyi.oss.enumd; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * minio策略配置 - * - * @author Lion Li - */ -@Getter -@AllArgsConstructor -public enum PolicyType { - - /** - * 只读 - */ - READ("read-only"), - - /** - * 只写 - */ - WRITE("write-only"), - - /** - * 读写 - */ - READ_WRITE("read-write"); - - /** - * 类型 - */ - private final String type; - -} diff --git a/ruoyi-oss/src/main/java/com/ruoyi/oss/exception/OssException.java b/ruoyi-oss/src/main/java/com/ruoyi/oss/exception/OssException.java deleted file mode 100644 index 540b1cc..0000000 --- a/ruoyi-oss/src/main/java/com/ruoyi/oss/exception/OssException.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.ruoyi.oss.exception; - -/** - * OSS异常类 - * - * @author Lion Li - */ -public class OssException extends RuntimeException { - - private static final long serialVersionUID = 1L; - - public OssException(String msg) { - super(msg); - } - -} diff --git a/ruoyi-oss/src/main/java/com/ruoyi/oss/factory/OssFactory.java b/ruoyi-oss/src/main/java/com/ruoyi/oss/factory/OssFactory.java deleted file mode 100644 index 78b5352..0000000 --- a/ruoyi-oss/src/main/java/com/ruoyi/oss/factory/OssFactory.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.ruoyi.oss.factory; - -import com.ruoyi.common.constant.CacheNames; -import com.ruoyi.common.utils.JsonUtils; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.common.utils.redis.CacheUtils; -import com.ruoyi.common.utils.redis.RedisUtils; -import com.ruoyi.oss.constant.OssConstant; -import com.ruoyi.oss.core.OssClient; -import com.ruoyi.oss.exception.OssException; -import com.ruoyi.oss.properties.OssProperties; -import lombok.extern.slf4j.Slf4j; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * 文件上传Factory - * - * @author Lion Li - */ -@Slf4j -public class OssFactory { - - private static final Map CLIENT_CACHE = new ConcurrentHashMap<>(); - - /** - * 获取默认实例 - */ - public static OssClient instance() { - // 获取redis 默认类型 - String configKey = RedisUtils.getCacheObject(OssConstant.DEFAULT_CONFIG_KEY); - if (StringUtils.isEmpty(configKey)) { - throw new OssException("文件存储服务类型无法找到!"); - } - return instance(configKey); - } - - /** - * 根据类型获取实例 - */ - public static OssClient instance(String configKey) { - String json = CacheUtils.get(CacheNames.SYS_OSS_CONFIG, configKey); - if (json == null) { - throw new OssException("系统异常, '" + configKey + "'配置信息不存在!"); - } - OssProperties properties = JsonUtils.parseObject(json, OssProperties.class); - OssClient client = CLIENT_CACHE.get(configKey); - if (client == null) { - CLIENT_CACHE.put(configKey, new OssClient(configKey, properties)); - log.info("创建OSS实例 key => {}", configKey); - return CLIENT_CACHE.get(configKey); - } - // 配置不相同则重新构建 - if (!client.checkPropertiesSame(properties)) { - CLIENT_CACHE.put(configKey, new OssClient(configKey, properties)); - log.info("重载OSS实例 key => {}", configKey); - return CLIENT_CACHE.get(configKey); - } - return client; - } - -} diff --git a/ruoyi-oss/src/main/java/com/ruoyi/oss/properties/OssProperties.java b/ruoyi-oss/src/main/java/com/ruoyi/oss/properties/OssProperties.java deleted file mode 100644 index 781a170..0000000 --- a/ruoyi-oss/src/main/java/com/ruoyi/oss/properties/OssProperties.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.ruoyi.oss.properties; - -import lombok.Data; - -/** - * OSS对象存储 配置属性 - * - * @author Lion Li - */ -@Data -public class OssProperties { - - /** - * 访问站点 - */ - private String endpoint; - - /** - * 自定义域名 - */ - private String domain; - - /** - * 前缀 - */ - private String prefix; - - /** - * ACCESS_KEY - */ - private String accessKey; - - /** - * SECRET_KEY - */ - private String secretKey; - - /** - * 存储空间名 - */ - private String bucketName; - - /** - * 存储区域 - */ - private String region; - - /** - * 是否https(Y=是,N=否) - */ - private String isHttps; - - /** - * 桶权限类型(0private 1public 2custom) - */ - private String accessPolicy; - -} diff --git a/ruoyi-system-cron/pom.xml b/ruoyi-system-cron/pom.xml index 70ecd4d..6b4f7be 100644 --- a/ruoyi-system-cron/pom.xml +++ b/ruoyi-system-cron/pom.xml @@ -31,7 +31,7 @@ com.ruoyi - ruoyi-system + ruoyi-common diff --git a/ruoyi-system-file/README.md b/ruoyi-system-file/README.md new file mode 100644 index 0000000..b80fc2b --- /dev/null +++ b/ruoyi-system-file/README.md @@ -0,0 +1 @@ +# 系统文件模块 \ No newline at end of file diff --git a/ruoyi-system-file/pom.xml b/ruoyi-system-file/pom.xml new file mode 100644 index 0000000..7fc7060 --- /dev/null +++ b/ruoyi-system-file/pom.xml @@ -0,0 +1,115 @@ + + + 4.0.0 + + com.ruoyi + ruoyi-vue-plus + 4.6.0 + + + ruoyi-system-file + + + 系统文件模块 + + + + + com.ruoyi + ruoyi-common + + + + com.github.gotson + webp-imageio + 0.2.2 + + + + org.dromara.x-file-storage + x-file-storage-spring + 2.2.1 + + + + + com.huaweicloud + esdk-obs-java + 3.22.12 + + + + com.aliyun.oss + aliyun-sdk-oss + 3.17.4 + + + + com.qiniu + qiniu-java-sdk + 7.12.1 + + + + com.qcloud + cos_api + 5.6.137 + + + + io.minio + minio + 8.5.2 + + + + + com.amazonaws + aws-java-sdk-s3 + 1.12.429 + + + + + com.squareup.okhttp3 + okhttp + 4.12.0 + + + + + com.jcraft + jsch + 0.1.55 + + + + commons-net + commons-net + 3.9.0 + + + + + cn.hutool + hutool-extra + + + + + org.apache.commons + commons-pool2 + 2.11.1 + + + + + com.github.lookfirst + sardine + 5.10 + + + + diff --git a/ruoyi-system-file/src/main/java/com/ruoyi/file/FileDownloadTestController.java b/ruoyi-system-file/src/main/java/com/ruoyi/file/FileDownloadTestController.java new file mode 100644 index 0000000..7a9781e --- /dev/null +++ b/ruoyi-system-file/src/main/java/com/ruoyi/file/FileDownloadTestController.java @@ -0,0 +1,30 @@ +package com.ruoyi.file; + +import cn.dev33.satoken.annotation.SaIgnore; +import com.ruoyi.common.annotation.Dev; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Controller; +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.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.time.Duration; + +@Controller +@RequiredArgsConstructor +@RequestMapping("/file/test/") +public class FileDownloadTestController { + + private final FileService fileService; + + @GetMapping("/download") + @SaIgnore + @Dev + public ModelAndView downloadFile(String url,String p, HttpServletRequest request, HttpServletResponse response) { + fileService.setPlatform(p).download(url, Duration.ofHours(1), request, response); + return null; + } +} diff --git a/ruoyi-system-file/src/main/java/com/ruoyi/file/FileService.java b/ruoyi-system-file/src/main/java/com/ruoyi/file/FileService.java new file mode 100644 index 0000000..3b86952 --- /dev/null +++ b/ruoyi-system-file/src/main/java/com/ruoyi/file/FileService.java @@ -0,0 +1,644 @@ +package com.ruoyi.file; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.img.Img; +import cn.hutool.core.img.ImgUtil; +import cn.hutool.core.lang.UUID; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.SecureUtil; +import cn.hutool.crypto.symmetric.SymmetricAlgorithm; +import cn.hutool.crypto.symmetric.SymmetricCrypto; +import com.ruoyi.common.utils.IdUtils; +import lombok.Data; +import lombok.SneakyThrows; +import org.apache.tika.Tika; +import org.dromara.x.file.storage.core.Downloader; +import org.dromara.x.file.storage.core.FileInfo; +import org.springframework.web.multipart.MultipartFile; + +import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.List; + + +public interface FileService { + + /** + * 生成保留文件名的规则 + */ + String RULE_KEEP_FILENAME = "/{yyyy}/{MM}/{dd}/{id36}/{filename}.{ext}"; + + /** + * 生成随机文件名的规则 + */ + String RULE_RANDOM_FILENAME = "/{yyyy}/{MM}/{dd}/{id36}.{ext}"; + + /** + * 默认前缀 + */ + String DEFAULT_PREFIX = "default"; + + + /** + * 默认图片文件名 + */ + String DEFAULT_IMAGE_FILENAME = "a.webp"; + + /** + * 缩略图的扩展名 + */ + String THUMBNAIL_EXT = ".min.webp"; + + /** + * 源文件的扩展名 + */ + String SRC_EXT = ".zip"; + + /** + * webp图片格式扩展名 + */ + String IMAGE_WEBP = "webp"; + + + ThreadLocal paramThreadLocal = ThreadLocal.withInitial(() -> new Param()); + + + /** + * 初始化分片上传 + * + * @return 分配上传编号 + */ + String multipartUploadInit(); + + /** + * 分片上传 + * + * @param uploadId + * @param partNumber + * @param inputStream + */ + void multipartUpload(String uploadId, Integer partNumber, InputStream inputStream); + + + /** + * 分片上传完成 + * + * @param uploadId + * @return + */ + String multipartUploadComplete(String uploadId); + + + /** + * 分片上传取消 + * + * @param uploadId + */ + void multipartUploadAbort(String uploadId); + + List multipartUploadListParts(String uploadId); + + /** + * 删除 + * + * @param url + */ + void delete(String url); + + /** + * 下载 + * + * @param url + * @return + */ + Downloader download(String url); + + /** + * 下载文件 + * 1.支持预签名的跳转 + * 2.本地模式的读取下载 + * 3.否则,直接读取下载 + * + * @param url url + * @param exp 过期时间 + * @param request 请求 + * @param response 响应 + */ + void download(String url, Duration exp, HttpServletRequest request, HttpServletResponse response); + + /** + * 生成预签名URL + * + * @param url 原始URL + * @param exp 过期时间 + * @return + */ + String generatePresignedUrl(String url, Duration exp); + + /** + * 在保存方法之前执行 + * 设置平台 + * + * @param platform 平台 + * @return + */ + default FileService setPlatform(String platform) { + paramThreadLocal.get().setPlatform(platform); + return this; + } + + /** + * 在保存方法之前执行 + * 设置路径前缀 + * + * @param prefix 前缀 + * @return + */ + default FileService setPrefix(String prefix) { + if (StrUtil.isBlank(prefix)) { + throw new RuntimeException("路径前缀不能为空"); + } + if (!prefix.matches("^[\\w\\-]+$")) { + throw new RuntimeException("路径前缀规则错误"); + } + paramThreadLocal.get().setPrefix(prefix); + return this; + } + + /** + * 在保存方法之前执行 + * 设置文件名 + * 保存inputstream流需要设置 + * + * @param filename + * @return + */ + default FileService setFilename(String filename) { + paramThreadLocal.get().setFilename(filename); + return this; + } + + /** + * 在保存方法之前执行 + * 使用RULE_KEEP_FILENAME规则生成保存路径 + * 不执行使用RULE_RANDOM_FILENAME规则生成保存路径 + * + * @return + */ + default FileService setKeepFilename() { + return setKeepFilename(true); + } + + /** + * 在保存方法之前执行 + * 使用RULE_KEEP_FILENAME规则生成保存路径 + * 不执行使用RULE_RANDOM_FILENAME规则生成保存路径 + * + * @return + */ + default FileService setKeepFilename(Boolean keepFilename) { + paramThreadLocal.get().setKeepFilename(keepFilename); + return this; + } + + /** + * 在保存方法之前执行 + * 设置保存路径,执行该方法后,setKeepFilename,setFilename,setRule将无效 + * + * @param uri 保存路径 + * @return + */ + default FileService setUri(String uri) { + paramThreadLocal.get().setUri(uri); + return this; + } + + /** + * 在保存方法之前执行 + * 使用rule规则生成保存路径 + * + * @param rule + * @return + */ + default FileService setRule(String rule) { + paramThreadLocal.get().setRule(rule); + return this; + } + + /** + * 在saveImage方法之前执行 + * 调整图片大小 + * + * @param maxWidth + * @param maxHeight + * @return + */ + default FileService setSize(int maxWidth, int maxHeight) { + paramThreadLocal.get().setMaxWidth(maxWidth); + paramThreadLocal.get().setMaxHeight(maxHeight); + return this; + } + + + /** + * 在saveImage方法之前执行 + * 调整图片大小(默认值) + * + * @return + */ + FileService setSize(); + + /** + * 在saveImage方法之前执行 + * 设置水印 + * + * @param watermark + * @return + */ + default FileService setWatermark(BufferedImage watermark) { + paramThreadLocal.get().setWatermark(watermark); + return this; + } + + /** + * 是否添加默认水印 + * @param watermark + * @return + */ + default FileService setWatermark(Boolean watermark) { + if(watermark){ + this.setWatermark(); + }else { + paramThreadLocal.get().setWatermark(null); + } + return this; + } + + /** + * 在saveImage方法之前执行 + * 设置默认水印 + * + * @return + */ + FileService setWatermark(); + + /** + * 在saveImage方法之前执行 + * 设置同时生成缩略图 + * + * @return + */ + default FileService setThumbnail() { + return setThumbnail(true); + } + + /** + * 在saveImage方法之前执行 + * 设置是否同时生成缩略图 + * + * @return + */ + FileService setThumbnail(boolean thumbnail); + + default FileService setSaveSrc(){ + return setSaveSrc(true); + } + + default FileService setSaveSrc(boolean saveSrc){ + paramThreadLocal.get().setSaveSrc(saveSrc); + return this; + } + + /** + * 在saveImage方法之前执行 + * 设置同时生成缩略图,并指定尺寸 + * + * @param width + * @param height + * @return + */ + default FileService setThumbnail(int width, int height) { + paramThreadLocal.get().setThWidth(width); + paramThreadLocal.get().setThHeight(height); + return this; + } + + + /** + * 保存文件 + * + * @param in + * @return + */ + String save(InputStream in); + + /** + * 保存文件 + * + * @param file + * @return + */ + @SneakyThrows + default String save(MultipartFile file) { + if (file == null || file.isEmpty()) { + throw new RuntimeException("文件不能为空"); + } + return setFilename(file.getOriginalFilename()).save(file.getInputStream()); + } + + /** + * 保存图片 + * + * @param in + * @return + */ + String saveImage(InputStream in); + + /** + * 保存图片 + * + * @param file + * @return + */ + @SneakyThrows + default String saveImage(MultipartFile file) { + if (file == null || file.isEmpty()) { + throw new RuntimeException("文件不能为空"); + } + return setFilename(file.getOriginalFilename()).saveImage(file.getInputStream()); + } + + + /** + * 获取类型提取工具 + * + * @return + */ + Tika getTika(); + + /** + * 获取文件类型 + * + * @param in + * @return + */ + @SneakyThrows + default String getContentType(InputStream in) { + return getTika().detect(in); + } + + /** + * 是否是图片文件 + * + * @param in + * @return + */ + @SneakyThrows + default boolean isImage(InputStream in) { + return getContentType(in).startsWith("image/"); + } + + + default InputStream formatImage(MultipartFile file, int maxWidth, int maxHeight, + BufferedImage watermark) { + try { + if (file == null || file.isEmpty()) { + throw new RuntimeException("上传图片不能为空"); + } + if (!file.getContentType().startsWith("image/")) { + throw new RuntimeException("上传的文件不是图片"); + } + return formatImage(file.getInputStream(), maxWidth, maxHeight, watermark); + } catch (IOException e) { + throw new RuntimeException("处理图片错误", e); + } + } + + default InputStream formatImage(Image img, int maxWidth, int maxHeight, BufferedImage watermark) { + if (img == null) { + throw new RuntimeException("图片不能为空"); + } + return formatImage(Img.from(img), maxWidth, maxHeight, watermark); + } + + default InputStream formatImage(InputStream in, int maxWidth, int maxHeight, BufferedImage watermark) { + return formatImage(Img.from(in), maxWidth, maxHeight, watermark); + } + + default InputStream formatImage(Img img, int maxWidth, int maxHeight, BufferedImage watermark) { + try { + int w = img.getImg().getWidth(null); + int h = img.getImg().getHeight(null); + if (maxWidth > 0 && maxHeight > 0) { + if (w > maxWidth || h > maxHeight) { + int outWidth = 0; + int outHeight = 0; + outHeight = maxWidth * h / w; + if (outHeight > maxHeight) { + outHeight = maxHeight; + outWidth = outHeight * w / h; + } else { + outWidth = maxWidth; + } + img = img.scale(outWidth, outHeight); + w = outWidth; + h = outHeight; + } + } + + + if (watermark != null) { + int ww = watermark.getWidth(null); + int wh = watermark.getHeight(null); + if (w > ww && h > wh) { + img = img.pressImage(watermark, 0, 0, 1f); + } + } + + + ByteArrayOutputStream pout = new ByteArrayOutputStream(); + +// img.setTargetImageType(IMAGE_WEBP).write(pout); + ImageIO.write(ImgUtil.toBufferedImage(img.getImg()), IMAGE_WEBP, pout); + + ByteArrayInputStream pin = new ByteArrayInputStream(pout.toByteArray()); + pout.close(); + pout = null; + return pin; + } catch (Exception e) { + throw new RuntimeException("处理图片错误", e); + } + } + + /** + * 生成存放的URI并保留文件名 + * + * @param filename 文件名 + * @param prefix 前缀 + * @return + */ + default String generateURIKeepFilename(String prefix, String filename) { + return generateURI(prefix, RULE_KEEP_FILENAME, filename); + } + + /** + * 生成存放的URI并保留文件名 + * + * @param filename 文件名 + * @return + */ + default String generateURIKeepFilename(String filename) { + return generateURI(DEFAULT_PREFIX, RULE_KEEP_FILENAME, filename); + } + + /** + * 生成存放的URI并随机文件名 + * + * @param filename 文件名 + * @param prefix 前缀 + * @return + */ + default String generateURIRandomFilename(String prefix, String filename) { + return generateURI(prefix, RULE_RANDOM_FILENAME, filename); + } + + + /** + * 生成存放的URI并随机文件名 + * + * @param filename 文件名 + * @return + */ + default String generateURIRandomFilename(String filename) { + return generateURI(DEFAULT_PREFIX, RULE_RANDOM_FILENAME, filename); + } + + /** + *
+   * - 根据存放规则和文件名生成存放的URI
+   * - 支持
+   * 	- {yyyy}/{MM}/{dd}/{HH}/{mm}/{ss} 年月日时分秒
+   * 	- {UUID} 32位的唯一标志
+   *  - {i} 自增id
+   * 	- {id} 当日int类型的唯一id,防止重复建议+年月日路径
+   * 	- {id16} 当日int类型的唯一id16进制表示,防止重复建议+年月日路径
+   * 	- {id36} 当日int类型的唯一id36进制表示,防止重复建议+年月日路径
+   * 	- {filename} 文件基础名称
+   * 	- {ext} 扩展名
+   * 
+ * + * @param prefix 前缀 + * @param rule 规则 + * @param filename 文件名 + * @return + */ + default String generateURI(String prefix, String rule, String filename) { + if (StrUtil.isBlank(prefix)) { + prefix = DEFAULT_PREFIX; + } + LocalDateTime now = LocalDateTime.now(); + if (rule.contains("{yyyy}")) { + rule = rule.replace("{yyyy}", "" + now.getYear()); + } + if (rule.contains("{MM}")) { + rule = rule.replace("{MM}", String.format("%02d", now.getMonthValue())); + } + if (rule.contains("{dd}")) { + rule = rule.replace("{dd}", String.format("%02d", now.getDayOfMonth())); + } + if (rule.contains("{HH}")) { + rule = rule.replace("{HH}", String.format("%02d", now.getHour())); + } + if (rule.contains("{mm}")) { + rule = rule.replace("{mm}", String.format("%02d", now.getMinute())); + } + if (rule.contains("{ss}")) { + rule = rule.replace("{ss}", String.format("%02d", now.getSecond())); + } + if (rule.contains("{UUID}")) { + rule = rule.replace("{UUID}", UUID.fastUUID().toString(true)); + } + if (rule.contains("{i}")) { + rule = rule.replace("{i}", IdUtils.nextId(Id.groupName).toString()); + } + if (rule.contains("{id}")) { + rule = rule.replace("{id}", Long.toString(id.nextId())); + } + if (rule.contains("{id16}")) { + rule = rule.replace("{id16}", Long.toString(id.nextId(), 16)); + } + if (rule.contains("{id36}")) { + rule = rule.replace("{id36}", Long.toString(id.nextId(), 36)); + } + if (rule.contains("{filename}")) { + String temp = null; + if (filename.contains(".")) { + temp = filename.substring(0, filename.lastIndexOf(".")); + } else { + temp = filename; + } + rule = rule.replace("{filename}", temp); + } + + if (rule.contains("{ext}")) { + String temp = null; + if (filename.contains(".")) { + temp = filename.substring(filename.lastIndexOf(".") + 1); + } else { + temp = ""; + } + rule = rule.replace("{ext}", temp.toLowerCase()); + } + return prefix + rule; + } + + Id id = new Id(); + + static class Id { + + private static final String groupName = "file:id"; + + private SymmetricCrypto crypto; + + private String today = DateUtil.today(); + + public synchronized Long nextId() { + if (crypto == null || !today.equals(DateUtil.today())) { + today = DateUtil.today(); + byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.DES.getValue(), today.getBytes()).getEncoded(); + crypto = new SymmetricCrypto(SymmetricAlgorithm.DES, key); + } + ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES); + buffer.putInt(IdUtils.nextDayId(groupName).intValue()); + return Math.abs(ByteBuffer.wrap(crypto.encrypt(buffer.array())).getLong()); + } + + } + + @Data + public static class Param { + private String platform; + private String prefix = FileService.DEFAULT_PREFIX; + private String filename; + private boolean keepFilename = false; + private boolean saveSrc = false; + private String rule; + private String uri; + private int maxWidth = 0; + private int maxHeight = 0; + private BufferedImage watermark; + private int thWidth = 0; + private int thHeight = 0; + + } +} diff --git a/ruoyi-system-file/src/main/java/com/ruoyi/file/FileUploadApi.java b/ruoyi-system-file/src/main/java/com/ruoyi/file/FileUploadApi.java new file mode 100644 index 0000000..aebbc26 --- /dev/null +++ b/ruoyi-system-file/src/main/java/com/ruoyi/file/FileUploadApi.java @@ -0,0 +1,184 @@ +package com.ruoyi.file; + +import cn.dev33.satoken.annotation.SaIgnore; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.ruoyi.common.annotation.Dev; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.file.FileUtils; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.InputStream; +import java.time.Duration; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/file/upload/") +@SaIgnore +public class FileUploadApi { + + public final FileService fileService; + + + /** + * 获取上传凭证, + * 实际业务中,需要在对应的业务中完成凭证的获取 + * + * @return + */ + @GetMapping("getUploadKey") + @Dev + public R getUploadKey() { + return R.ok().setData(FileUtils.getUploadKey()); + } + + /** + * 删除上传凭证 + * + * @param key + */ + @GetMapping("removeUploadKey-{key}") + public void removeUploadKey(@PathVariable String key) { + FileUtils.removeUploadKey(key); + } + + @GetMapping("exitisUploadKey-{key}") + public Boolean exitisUploadKey(@PathVariable String key) { + return FileUtils.exitisUploadKey(key); + } + + + /** + * 上传文件 + * + * @param file + * @param prefix + * @param key + * @param keepFilename + * @return + */ + @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public R upload(@RequestPart("file") MultipartFile file, String prefix, String key, @RequestParam(defaultValue = "false") Boolean keepFilename) { + if (ObjectUtil.isNull(file)) { + throw new ServiceException("文件必填"); + } + if (!FileUtils.exitisUploadKey(key)) { + throw new ServiceException("上传凭证无效"); + } + return R.ok().setData(fileService.setPrefix(prefix).setKeepFilename(keepFilename).save(file)); + } + + + @PostMapping("multipartUploadInit") + public R multipartUploadInit(String filename, String prefix, String key, @RequestParam(defaultValue = "false") Boolean keepFilename) { + if (StrUtil.isBlank(filename)) { + throw new ServiceException("文件名必填"); + } + if (!FileUtils.exitisUploadKey(key)) { + throw new ServiceException("上传凭证无效"); + } + return R.ok().setData(fileService.setPrefix(prefix).setFilename(filename).setKeepFilename(keepFilename).multipartUploadInit()); + } + + @PostMapping(value = "multipartUpload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @SneakyThrows + public R multipartUpload(@RequestPart("file") MultipartFile file, String uploadId, Integer partNumber) { + if (StrUtil.isBlank(uploadId)) { + throw new ServiceException("上传标识必填"); + } + fileService.multipartUpload(uploadId, partNumber, file.getInputStream()); + return R.ok(); + } + + @GetMapping("multipartUploadListParts") + public R multipartUploadListParts(String uploadId) { + if (StrUtil.isBlank(uploadId)) { + throw new ServiceException("上传标识必填"); + } + return R.ok().setData(fileService.multipartUploadListParts(uploadId)); + } + + @PostMapping("multipartUploadComplete") + public R multipartUploadComplete(String uploadId) { + if (StrUtil.isBlank(uploadId)) { + throw new ServiceException("上传标识必填"); + } + return R.ok().setData(fileService.multipartUploadComplete(uploadId)); + } + + @PostMapping("multipartUploadAbort") + public R multipartUploadAbort(String uploadId) { + if (StrUtil.isBlank(uploadId)) { + throw new ServiceException("上传标识必填"); + } + try { + fileService.multipartUploadAbort(uploadId); + } catch (Exception e) { + + } + return R.ok(); + } + + + /** + * 上传图片 + * + * @param file + * @param prefix + * @param key + * @param saveSrc + * @param saveThumbnail + * @return + */ + @PostMapping(value = "image", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public R uploadImage(@RequestPart("file") MultipartFile file, String prefix, String key, + @RequestParam(defaultValue = "false") Boolean saveSrc, + @RequestParam(defaultValue = "true") Boolean watermark, + @RequestParam(defaultValue = "true") Boolean saveThumbnail + ) { + if (ObjectUtil.isNull(file)) { + throw new ServiceException("文件必填"); + } + if (!FileUtils.exitisUploadKey(key)) { + throw new ServiceException("上传凭证无效"); + } + + return R.ok().setData(fileService.setPrefix(prefix).setWatermark(watermark).setSaveSrc(saveSrc).setThumbnail(saveThumbnail).saveImage(file)); + } + + @GetMapping("/download") + @SneakyThrows + public ModelAndView downloadFile(String url, String key, HttpServletRequest request, HttpServletResponse response) { + if (StrUtil.isBlank(url)) { + response.sendError(404, "URL不能为空"); + return null; + } + if (!FileUtils.exitisUploadKey(key)) { + response.sendError(404, "上传凭证无效"); + return null; + } + fileService.download(url, Duration.ofHours(1), request, response); + return null; + } + + @PostMapping("/remove") + public R remove(String url, String key){ + if (StrUtil.isBlank(url)) { + throw new ServiceException("URL不能为空"); + } + if (!FileUtils.exitisUploadKey(key)) { + throw new ServiceException("上传凭证无效"); + } + fileService.delete(url); + return R.ok(); + } + +} diff --git a/ruoyi-system-file/src/main/java/com/ruoyi/file/config/FileConfig.java b/ruoyi-system-file/src/main/java/com/ruoyi/file/config/FileConfig.java new file mode 100644 index 0000000..3b57c0d --- /dev/null +++ b/ruoyi-system-file/src/main/java/com/ruoyi/file/config/FileConfig.java @@ -0,0 +1,18 @@ +package com.ruoyi.file.config; + + +import org.dromara.x.file.storage.spring.EnableFileStorage; +import org.dromara.x.file.storage.spring.FileStorageAutoConfiguration; +import org.dromara.x.file.storage.spring.SpringFileStorageProperties; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +@Configuration +//@EnableFileStorage +@Import({FileStorageAutoConfiguration.class}) +public class FileConfig { + + +} diff --git a/ruoyi-system-file/src/main/java/com/ruoyi/file/config/SpringFileStorageProperties.java b/ruoyi-system-file/src/main/java/com/ruoyi/file/config/SpringFileStorageProperties.java new file mode 100644 index 0000000..09c4a82 --- /dev/null +++ b/ruoyi-system-file/src/main/java/com/ruoyi/file/config/SpringFileStorageProperties.java @@ -0,0 +1,45 @@ +package com.ruoyi.file.config; + + +import cn.hutool.core.img.ImgUtil; +import lombok.AccessLevel; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.context.properties.ConfigurationProperties; + +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.awt.image.BufferedImage; +import java.io.File; + + +@Configuration +@ConfigurationProperties(prefix = "ruoyi.file") +@Data +@Slf4j +public class SpringFileStorageProperties extends org.dromara.x.file.storage.spring.SpringFileStorageProperties { + + private Integer maxWidth; + private Integer maxHeight; + private File watermark; + private Integer thWidth; + private Integer thHeight; + @Getter + @Setter(AccessLevel.PRIVATE) + private BufferedImage watermarkImage; + + @PostConstruct + public void init() { + try { + if (watermark != null && watermark.isFile()) { + watermarkImage = ImgUtil.read(watermark); + log.warn("水印图片加载成功:" + watermark.toString()); + } + } catch (Exception e) { + log.warn("初始化水印失败", e); + } + } +} diff --git a/ruoyi-system-file/src/main/java/com/ruoyi/file/impl/FileServiceImpl.java b/ruoyi-system-file/src/main/java/com/ruoyi/file/impl/FileServiceImpl.java new file mode 100644 index 0000000..7b95183 --- /dev/null +++ b/ruoyi-system-file/src/main/java/com/ruoyi/file/impl/FileServiceImpl.java @@ -0,0 +1,370 @@ +package com.ruoyi.file.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.img.ImgUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.lang.UUID; +import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.URLUtil; +import cn.hutool.core.util.ZipUtil; +import com.ruoyi.common.utils.HttpDownloadUtil; +import com.ruoyi.common.utils.file.FileUtils; +import com.ruoyi.common.utils.redis.RedisUtils; +import com.ruoyi.file.FileService; +import com.ruoyi.file.config.SpringFileStorageProperties; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.apache.tika.Tika; +import org.dromara.x.file.storage.core.Downloader; +import org.dromara.x.file.storage.core.FileInfo; +import org.dromara.x.file.storage.core.FileStorageService; +import org.dromara.x.file.storage.core.constant.Constant; +import org.dromara.x.file.storage.core.platform.FileStorage; +import org.dromara.x.file.storage.core.platform.LocalPlusFileStorage; + +import org.dromara.x.file.storage.core.platform.UpyunUssFileStorage; +import org.dromara.x.file.storage.core.presigned.GeneratePresignedUrlResult; +import org.dromara.x.file.storage.core.tika.TikaFactory; +import org.dromara.x.file.storage.core.upload.FilePartInfo; +import org.dromara.x.file.storage.core.upload.MultipartUploadSupportInfo; +import org.dromara.x.file.storage.core.upload.UploadPretreatment; +import org.springframework.stereotype.Service; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.awt.*; +import java.io.*; +import java.time.Duration; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +@Service +@Slf4j +@RequiredArgsConstructor +public class FileServiceImpl implements FileService { + + private final FileStorageService service; + private final SpringFileStorageProperties properties; + private final TikaFactory tikaFactory; + public static final String CACHE_KEY_PREFIX = "file:multipart:"; + + + @Override + public String multipartUploadInit() { + try { + + UploadPretreatment p = new UploadPretreatment(); + Param param = paramThreadLocal.get(); + initUploadPretreatment(p, param); + FileStorage fileStorage = service.getFileStorage(p.getPlatform()); + MultipartUploadSupportInfo supportInfo = fileStorage.isSupportMultipartUpload(); + if (!(supportInfo.getIsSupport() && supportInfo.getIsSupportAbort() && supportInfo.getIsSupportListParts())) { + throw new RuntimeException("不支持分片上传"); + } + boolean isUpyunUss = fileStorage instanceof UpyunUssFileStorage; + FileInfo fileInfo = service.initiateMultipartUpload() + .setPlatform(p.getPlatform()) + .setPath(p.getPath()) + .setSaveFilename(p.getSaveFilename()) + .putMetadata(isUpyunUss, "X-Upyun-Multi-Part-Size", String.valueOf(5 * 1024 * 1024))// 设置 Metadata,不需要可以不写 + .init(); + + String uploadId = UUID.fastUUID().toString(true); + RedisUtils.setCacheObject(CACHE_KEY_PREFIX + uploadId, fileInfo, Duration.ofMinutes(60)); + return uploadId; + } finally { + paramThreadLocal.remove(); + } + } + + @Override + public void multipartUpload(String uploadId, Integer partNumber, InputStream inputStream) { + FileInfo fileInfo = RedisUtils.getCacheObject(CACHE_KEY_PREFIX + uploadId); + if (fileInfo == null) { + throw new RuntimeException("未初始化分片上传"); + } + service.uploadPart(fileInfo, partNumber, inputStream).upload(); + } + + + @Override + public String multipartUploadComplete(String uploadId) { + FileInfo fileInfo = RedisUtils.getCacheObject(CACHE_KEY_PREFIX + uploadId); + if (fileInfo == null) { + throw new RuntimeException("未初始化分片上传"); + } + service.completeMultipartUpload(fileInfo).complete(); + RedisUtils.deleteObject(CACHE_KEY_PREFIX + uploadId); + return fileInfo.getUrl(); + } + + @Override + public void multipartUploadAbort(String uploadId) { + FileInfo fileInfo = RedisUtils.getCacheObject(CACHE_KEY_PREFIX + uploadId); + + if (fileInfo == null) { + throw new RuntimeException("未初始化分片上传"); + } + service.abortMultipartUpload(fileInfo).abort(); + RedisUtils.deleteObject(CACHE_KEY_PREFIX + uploadId); + } + + @Override + public List multipartUploadListParts(String uploadId) { + FileInfo fileInfo = RedisUtils.getCacheObject(CACHE_KEY_PREFIX + uploadId); + return service.listParts(fileInfo).listParts().getList().stream().map(FilePartInfo::getPartNumber).collect(Collectors.toList()); + } + + @Override + public void delete(String url) { + try { + FileInfo fileInfo = getFileInfoByURL(url); + service.delete(fileInfo); + } finally { + paramThreadLocal.remove(); + } + } + + @Override + public Downloader download(String url) { + try { + FileInfo fileInfo = getFileInfoByURL(url); + return service.download(fileInfo); + } finally { + paramThreadLocal.remove(); + } + } + + @SneakyThrows + public void download(String url, Duration exp, HttpServletRequest request, HttpServletResponse response) { + + try { + + FileInfo fileInfo = getFileInfoByURL(url); + FileStorage fileStorage = service.getFileStorage(fileInfo.getPlatform()); + + if (fileStorage.isSupportPresignedUrl()) { + url = generatePresignedUrlInner(url, null, fileInfo, fileStorage); + log.debug("跳转:"+url); + response.reset(); + response.setStatus(HttpServletResponse.SC_FOUND); + response.setHeader("Location", url); + response.getWriter().print(""); + response.flushBuffer(); + } else { + if (fileStorage instanceof LocalPlusFileStorage) { + LocalPlusFileStorage fs = (LocalPlusFileStorage) fileStorage; + File file = new File(new File(fs.getStoragePath(), fileInfo.getPath()), fileInfo.getFilename()); + log.debug("下载本地文件:" + file.getAbsolutePath()); + if (!file.isFile()) { + throw new FileNotFoundException("文件不存在"); + } + HttpDownloadUtil.download(request, response, file); + + } else { + response.reset(); + FileUtils.setAttachmentResponseHeader(response, fileInfo.getFilename()); + service.download(fileInfo).outputStream(response.getOutputStream()); + response.flushBuffer(); + } + } + + + } catch (Exception e) { + log.warn("下载文件失败", e); + response.sendError(404, "下载文件失败"); + } finally { + paramThreadLocal.remove(); + } + } + + @Override + @SneakyThrows + public String generatePresignedUrl(String url, Duration exp) { + try { + FileInfo fileInfo = getFileInfoByURL(url); + FileStorage fileStorage = service.getFileStorage(fileInfo.getPlatform()); + return generatePresignedUrlInner(url, exp, fileInfo, fileStorage); + } finally { + paramThreadLocal.remove(); + } + } + + @SneakyThrows + private String generatePresignedUrlInner(String url, Duration exp, FileInfo fileInfo, FileStorage fileStorage) { + if (!fileStorage.isSupportPresignedUrl()) { + throw new RuntimeException("不支持预签名URL"); + } + GeneratePresignedUrlResult downloadResult = service.generatePresignedUrl() + .setPlatform(fileInfo.getPlatform()) // 存储平台,不传使用默认的 + .setPath(fileInfo.getPath()) // 文件路径 + .setFilename(fileInfo.getFilename()) // 文件名,也可以换成缩略图的文件名 + .setMethod(Constant.GeneratePresignedUrl.Method.GET) // 签名方法 + .setExpiration(exp == null ? new Date(System.currentTimeMillis() + 600000) : new Date(System.currentTimeMillis() + exp.toMillis())) // 过期时间 10 分钟 + .putResponseHeaders( + Constant.Metadata.CONTENT_DISPOSITION, "attachment;filename=" + (fileInfo.getFilename().endsWith("." + FileService.IMAGE_WEBP + FileService.SRC_EXT) ? "原图.zip" : fileInfo.getFilename())) + + .generatePresignedUrl(); + log.debug("生成访问预签名 URL 结果:{}", downloadResult); + String q = downloadResult.getUrl().substring(downloadResult.getUrl().indexOf("?")); + return URLUtil.encode(url) + q; + } + + @Override + public FileService setSize() { + return setSize(properties.getMaxWidth(), properties.getMaxHeight()); + } + + @Override + public FileService setWatermark() { + return setWatermark(properties.getWatermarkImage()); + } + + @Override + public FileService setThumbnail(boolean thumbnail) { + if (thumbnail) { + return setThumbnail(properties.getThWidth(), properties.getThHeight()); + } else { + return setThumbnail(0, 0); + } + } + + @Override + @SneakyThrows + public String save(InputStream in) { + try { + UploadPretreatment p = service.of(in); + Param param = paramThreadLocal.get(); + initUploadPretreatment(p, param); + return p.upload().getUrl(); + } finally { + paramThreadLocal.remove(); + } + } + + + @Override + @SneakyThrows + public String saveImage(InputStream in) { + try { + byte[] imageBytes = IoUtil.readBytes(in, true); + Image image = ImgUtil.read(new ByteArrayInputStream(imageBytes)); + Param param = paramThreadLocal.get(); + param.setKeepFilename(false); + String srcFilename = param.getFilename(); + param.setFilename(FileService.DEFAULT_IMAGE_FILENAME); + UploadPretreatment p = service.of(formatImage(image, param.getMaxWidth(), param.getMaxHeight(), param.getWatermark())); + initUploadPretreatment(p, param); + String url = p.upload().getUrl(); + if (param.getThHeight() > 0 && param.getThWidth() > 0) { + UploadPretreatment p1 = service.of(formatImage(image, param.getThWidth(), param.getThHeight(), null)); + String platform = param.getPlatform(); + if (StrUtil.isNotBlank(platform)) { + p1 = p1.setPlatform(platform); + } + p1.setPath(p.getPath()); + p1.setSaveFilename(p.getSaveFilename() + FileService.THUMBNAIL_EXT); + p1.upload(); + } + + if (param.isSaveSrc() && StrUtil.isNotBlank(srcFilename)) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ZipUtil.zip(out, new String[]{srcFilename}, new InputStream[]{new ByteArrayInputStream(imageBytes)}); + UploadPretreatment p1 = service.of(new ByteArrayInputStream(out.toByteArray())); + String platform = param.getPlatform(); + if (StrUtil.isNotBlank(platform)) { + p1 = p1.setPlatform(platform); + } + p1.setPath(p.getPath()); + p1.setSaveFilename(p.getSaveFilename() + FileService.SRC_EXT); + p1.upload(); + } + + return url; + } finally { + paramThreadLocal.remove(); + } + } + + + @Override + public Tika getTika() { + return tikaFactory.getTika(); + } + + private FileInfo getFileInfoByURL(String url) { + if (StrUtil.isBlank(url)) { + throw new RuntimeException("URL不能为空"); + } + FileInfo fileInfo = new FileInfo(); + String platform = paramThreadLocal.get().getPlatform(); + FileStorage fileStorage = null; + if (StrUtil.isBlank(platform)) { + fileStorage = service.getFileStorage(); + } else { + fileStorage = service.getFileStorage(platform); + } + fileInfo.setPlatform(fileStorage.getPlatform()); + String domain = null; + try { + domain = (String) BeanUtil.getProperty(fileStorage, "domain"); + } catch (Exception e) { + } + if (StrUtil.isBlank(domain)) { + int index = url.indexOf("://"); + if (index > -1) { + url = url.substring(url.indexOf("/", index + 4) + 1); + } else if (url.startsWith("//")) { + url = url.substring(url.indexOf("/", 3) + 1); + } else if (url.startsWith("/")) { + url = url.substring(1); + } + index = url.indexOf("/"); + url = url.substring(index + 1); + index = url.lastIndexOf("/"); + + fileInfo.setPath(url.substring(0, index + 1)); + fileInfo.setFilename(url.substring(index + 1)); + } else { + url = url.substring(domain.length()); + int index = url.lastIndexOf("/"); + fileInfo.setPath(url.substring(0, index + 1)); + fileInfo.setFilename(url.substring(index + 1)); + } + + + return fileInfo; + } + + private void initUploadPretreatment(UploadPretreatment p, Param param) { + + String platform = param.getPlatform(); + if (StrUtil.isNotBlank(platform)) { + p = p.setPlatform(platform); + } else { + p = p.setPlatform(service.getFileStorage().getPlatform()); + } + String uri = param.getUri(); + if (StrUtil.isBlank(uri)) { + String rule = param.getRule(); + if (StrUtil.isBlank(rule)) { + if (param.isKeepFilename()) { + rule = RULE_KEEP_FILENAME; + } else { + rule = RULE_RANDOM_FILENAME; + } + } + uri = generateURI(param.getPrefix(), rule, param.getFilename()); + } else { + uri = param.getPrefix() + uri; + } + + int index = uri.lastIndexOf("/") + 1; + String path = uri.substring(0, index); + String filename = uri.substring(index); + log.debug("path={},filename={}", path, filename); + p.setPath(path).setSaveFilename(filename); + } +} diff --git a/ruoyi-system-file/src/main/java/com/ruoyi/file/impl/FixFileStorageAspect.java b/ruoyi-system-file/src/main/java/com/ruoyi/file/impl/FixFileStorageAspect.java new file mode 100644 index 0000000..e2e0658 --- /dev/null +++ b/ruoyi-system-file/src/main/java/com/ruoyi/file/impl/FixFileStorageAspect.java @@ -0,0 +1,31 @@ +package com.ruoyi.file.impl; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.x.file.storage.core.FileInfo; +import org.dromara.x.file.storage.core.FileStorageService; +import org.dromara.x.file.storage.core.aspect.CompleteMultipartUploadAspectChain; +import org.dromara.x.file.storage.core.aspect.FileStorageAspect; +import org.dromara.x.file.storage.core.aspect.ListPartsAspectChain; +import org.dromara.x.file.storage.core.platform.AliyunOssFileStorage; +import org.dromara.x.file.storage.core.platform.FileStorage; +import org.dromara.x.file.storage.core.recorder.FileRecorder; +import org.dromara.x.file.storage.core.tika.ContentTypeDetect; +import org.dromara.x.file.storage.core.upload.CompleteMultipartUploadPretreatment; +import org.dromara.x.file.storage.core.upload.FilePartInfoList; +import org.dromara.x.file.storage.core.upload.ListPartsPretreatment; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +@RequiredArgsConstructor +public class FixFileStorageAspect implements FileStorageAspect { + + @Override + public FilePartInfoList listParts(ListPartsAspectChain chain, ListPartsPretreatment pre, FileStorage fileStorage) { + if(fileStorage instanceof AliyunOssFileStorage) { + pre.setPartNumberMarker(null); + } + return chain.next(pre,fileStorage); + } +} diff --git a/ruoyi-system-file/src/main/java/com/ruoyi/file/package-info.java b/ruoyi-system-file/src/main/java/com/ruoyi/file/package-info.java new file mode 100644 index 0000000..054fc7e --- /dev/null +++ b/ruoyi-system-file/src/main/java/com/ruoyi/file/package-info.java @@ -0,0 +1,4 @@ +/** + * 文件模块 + */ +package com.ruoyi.file; diff --git a/ruoyi-system-file/src/main/resources/watermark.png b/ruoyi-system-file/src/main/resources/watermark.png new file mode 100644 index 0000000..aec18b5 Binary files /dev/null and b/ruoyi-system-file/src/main/resources/watermark.png differ diff --git a/ruoyi-system/pom.xml b/ruoyi-system/pom.xml index 6a7f2af..44fd9e3 100644 --- a/ruoyi-system/pom.xml +++ b/ruoyi-system/pom.xml @@ -23,11 +23,7 @@ ruoyi-common - - - com.ruoyi - ruoyi-oss - + @@ -35,10 +31,6 @@ ruoyi-sms - - com.github.gotson - webp-imageio - diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/config/DownloadFileConfig.java b/ruoyi-system/src/main/java/com/ruoyi/system/config/DownloadFileConfig.java deleted file mode 100644 index 1833821..0000000 --- a/ruoyi-system/src/main/java/com/ruoyi/system/config/DownloadFileConfig.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.ruoyi.system.config; - -import cn.hutool.core.io.file.FileNameUtil; -import cn.hutool.core.util.URLUtil; -import com.ruoyi.common.config.RuoYiConfig; -import com.ruoyi.common.utils.HttpDownloadUtil; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.web.servlet.ServletRegistrationBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.File; -import java.io.IOException; - -/** - * 本地文件公开下载配置 - * 方式一:SpringBoot静态资源配置 - * 方式二:Servlet - */ -@Configuration -@RequiredArgsConstructor -@Slf4j -public class DownloadFileConfig { - - private final RuoYiConfig config; - - @Bean - public WebMvcConfigurer DownloadFileWebMvcConfigurer() { - return new WebMvcConfigurer(){ - - @Override - public void addResourceHandlers(ResourceHandlerRegistry registry) { - //加入外部静态资源html文件夹 - String path = new File(new File(config.upload.savePath).getAbsolutePath()).toURI().toString(); - registry.addResourceHandler(config.upload.pre+"/**").addResourceLocations(path); - log.info("添加静态资源目: {}/** ={}",config.upload.pre,path); - - } - }; - } - - - public ServletRegistrationBean DownloadFileServletRegistrationBean() { - ServletRegistrationBean bean = new ServletRegistrationBean(); - bean.setServlet(new HttpServlet() { - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - String uri = req.getRequestURI().substring(config.upload.pre.length()); - uri = URLUtil.decode(uri); - File down = new File(config.upload.savePath, uri); - if(down.isFile()) { - try { - HttpDownloadUtil.download(req, resp,down, - FileNameUtil.getName(uri)); - resp.getOutputStream().close(); - } catch (Exception e) { - resp.reset(); - resp.sendError(404); - } - }else { - resp.sendError(404); - } - } - }); - bean.addUrlMappings(config.upload.pre+"/*"); - return bean; - } -} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOss.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOss.java deleted file mode 100644 index 968304b..0000000 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOss.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.ruoyi.system.domain; - -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import com.ruoyi.common.core.domain.BaseEntity; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * OSS对象存储对象 - * - * @author Lion Li - */ -@Data -@EqualsAndHashCode(callSuper = true) -@TableName("sys_oss") -public class SysOss extends BaseEntity { - - /** - * 对象存储主键 - */ - @TableId(value = "oss_id") - private Long ossId; - - /** - * 文件名 - */ - private String fileName; - - /** - * 原名 - */ - private String originalName; - - /** - * 文件后缀名 - */ - private String fileSuffix; - - /** - * URL地址 - */ - private String url; - - /** - * 服务商 - */ - private String service; - -} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOssConfig.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOssConfig.java deleted file mode 100644 index ac5e5a3..0000000 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOssConfig.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.ruoyi.system.domain; - -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import com.ruoyi.common.core.domain.BaseEntity; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 对象存储配置对象 sys_oss_config - * - * @author Lion Li - */ -@Data -@EqualsAndHashCode(callSuper = true) -@TableName("sys_oss_config") -public class SysOssConfig extends BaseEntity { - - /** - * 主建 - */ - @TableId(value = "oss_config_id") - private Long ossConfigId; - - /** - * 配置key - */ - private String configKey; - - /** - * accessKey - */ - private String accessKey; - - /** - * 秘钥 - */ - private String secretKey; - - /** - * 桶名称 - */ - private String bucketName; - - /** - * 前缀 - */ - private String prefix; - - /** - * 访问站点 - */ - private String endpoint; - - /** - * 自定义域名 - */ - private String domain; - - /** - * 是否https(0否 1是) - */ - private String isHttps; - - /** - * 域 - */ - private String region; - - /** - * 是否默认(0=是,1=否) - */ - private String status; - - /** - * 扩展字段 - */ - private String ext1; - - /** - * 备注 - */ - private String remark; - - /** - * 桶权限类型(0private 1public 2custom) - */ - private String accessPolicy; -} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssBo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssBo.java deleted file mode 100644 index f874b20..0000000 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssBo.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.ruoyi.system.domain.bo; - -import com.ruoyi.common.core.domain.BaseEntity; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * OSS对象存储分页查询对象 sys_oss - * - * @author Lion Li - */ -@Data -@EqualsAndHashCode(callSuper = true) -public class SysOssBo extends BaseEntity { - - /** - * ossId - */ - private Long ossId; - - /** - * 文件名 - */ - private String fileName; - - /** - * 原名 - */ - private String originalName; - - /** - * 文件后缀名 - */ - private String fileSuffix; - - /** - * URL地址 - */ - private String url; - - /** - * 服务商 - */ - private String service; - -} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssConfigBo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssConfigBo.java deleted file mode 100644 index 2e257e8..0000000 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssConfigBo.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.ruoyi.system.domain.bo; - -import com.ruoyi.common.core.domain.BaseEntity; -import com.ruoyi.common.core.validate.AddGroup; -import com.ruoyi.common.core.validate.EditGroup; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; - -/** - * 对象存储配置业务对象 sys_oss_config - * - * @author Lion Li - * @author 孤舟烟雨 - * @date 2021-08-13 - */ - -@Data -@EqualsAndHashCode(callSuper = true) -public class SysOssConfigBo extends BaseEntity { - - /** - * 主建 - */ - @NotNull(message = "主建不能为空", groups = {EditGroup.class}) - private Long ossConfigId; - - /** - * 配置key - */ - @NotBlank(message = "配置key不能为空", groups = {AddGroup.class, EditGroup.class}) - @Size(min = 2, max = 100, message = "configKey长度必须介于{min}和{max} 之间") - private String configKey; - - /** - * accessKey - */ - @NotBlank(message = "accessKey不能为空", groups = {AddGroup.class, EditGroup.class}) - @Size(min = 2, max = 100, message = "accessKey长度必须介于{min}和{max} 之间") - private String accessKey; - - /** - * 秘钥 - */ - @NotBlank(message = "secretKey不能为空", groups = {AddGroup.class, EditGroup.class}) - @Size(min = 2, max = 100, message = "secretKey长度必须介于{min}和{max} 之间") - private String secretKey; - - /** - * 桶名称 - */ - @NotBlank(message = "桶名称不能为空", groups = {AddGroup.class, EditGroup.class}) - @Size(min = 2, max = 100, message = "bucketName长度必须介于{min}和{max}之间") - private String bucketName; - - /** - * 前缀 - */ - private String prefix; - - /** - * 访问站点 - */ - @NotBlank(message = "访问站点不能为空", groups = {AddGroup.class, EditGroup.class}) - @Size(min = 2, max = 100, message = "endpoint长度必须介于{min}和{max}之间") - private String endpoint; - - /** - * 自定义域名 - */ - private String domain; - - /** - * 是否https(Y=是,N=否) - */ - private String isHttps; - - /** - * 是否默认(0=是,1=否) - */ - private String status; - - /** - * 域 - */ - private String region; - - /** - * 扩展字段 - */ - private String ext1; - - /** - * 备注 - */ - private String remark; - - /** - * 桶权限类型(0private 1public 2custom) - */ - @NotBlank(message = "桶权限类型不能为空", groups = {AddGroup.class, EditGroup.class}) - private String accessPolicy; - -} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOssConfigVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOssConfigVo.java deleted file mode 100644 index 9edc60c..0000000 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOssConfigVo.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.ruoyi.system.domain.vo; - -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import lombok.Data; - - -/** - * 对象存储配置视图对象 sys_oss_config - * - * @author Lion Li - * @author 孤舟烟雨 - * @date 2021-08-13 - */ -@Data -@ExcelIgnoreUnannotated -public class SysOssConfigVo { - - private static final long serialVersionUID = 1L; - - /** - * 主建 - */ - private Long ossConfigId; - - /** - * 配置key - */ - private String configKey; - - /** - * accessKey - */ - private String accessKey; - - /** - * 秘钥 - */ - private String secretKey; - - /** - * 桶名称 - */ - private String bucketName; - - /** - * 前缀 - */ - private String prefix; - - /** - * 访问站点 - */ - private String endpoint; - - /** - * 自定义域名 - */ - private String domain; - - /** - * 是否https(Y=是,N=否) - */ - private String isHttps; - - /** - * 域 - */ - private String region; - - /** - * 是否默认(0=是,1=否) - */ - private String status; - - /** - * 扩展字段 - */ - private String ext1; - - /** - * 备注 - */ - private String remark; - - /** - * 桶权限类型(0private 1public 2custom) - */ - private String accessPolicy; - -} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOssVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOssVo.java deleted file mode 100644 index 53f5f8d..0000000 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOssVo.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.ruoyi.system.domain.vo; - -import lombok.Data; - -import java.util.Date; - -/** - * OSS对象存储视图对象 sys_oss - * - * @author Lion Li - */ -@Data -public class SysOssVo { - - private static final long serialVersionUID = 1L; - - /** - * 对象存储主键 - */ - private Long ossId; - - /** - * 文件名 - */ - private String fileName; - - /** - * 原名 - */ - private String originalName; - - /** - * 文件后缀名 - */ - private String fileSuffix; - - /** - * URL地址 - */ - private String url; - - /** - * 创建时间 - */ - private Date createTime; - - /** - * 上传人 - */ - private String createBy; - - /** - * 服务商 - */ - private String service; - - -} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOssConfigMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOssConfigMapper.java deleted file mode 100644 index 72f29a7..0000000 --- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOssConfigMapper.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.ruoyi.system.mapper; - -import com.ruoyi.common.core.mapper.BaseMapperPlus; -import com.ruoyi.system.domain.SysOssConfig; -import com.ruoyi.system.domain.vo.SysOssConfigVo; - -/** - * 对象存储配置Mapper接口 - * - * @author Lion Li - * @author 孤舟烟雨 - * @date 2021-08-13 - */ -public interface SysOssConfigMapper extends BaseMapperPlus { - -} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOssMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOssMapper.java deleted file mode 100644 index edbaed6..0000000 --- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOssMapper.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.ruoyi.system.mapper; - -import com.ruoyi.common.core.mapper.BaseMapperPlus; -import com.ruoyi.system.domain.SysOss; -import com.ruoyi.system.domain.vo.SysOssVo; - -/** - * 文件上传 数据层 - * - * @author Lion Li - */ -public interface SysOssMapper extends BaseMapperPlus { -} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/runner/SystemApplicationRunner.java b/ruoyi-system/src/main/java/com/ruoyi/system/runner/SystemApplicationRunner.java index e9ad6ee..ffeb516 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/runner/SystemApplicationRunner.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/runner/SystemApplicationRunner.java @@ -3,7 +3,6 @@ package com.ruoyi.system.runner; import com.ruoyi.common.config.RuoYiConfig; import com.ruoyi.system.service.ISysConfigService; import com.ruoyi.system.service.ISysDictTypeService; -import com.ruoyi.system.service.ISysOssConfigService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.ApplicationArguments; @@ -23,11 +22,10 @@ public class SystemApplicationRunner implements ApplicationRunner { private final RuoYiConfig ruoyiConfig; private final ISysConfigService configService; private final ISysDictTypeService dictTypeService; - private final ISysOssConfigService ossConfigService; @Override public void run(ApplicationArguments args) throws Exception { - ossConfigService.init(); + log.info("初始化OSS配置成功"); if (ruoyiConfig.isCacheLazy()) { return; diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/FileService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/FileService.java deleted file mode 100644 index 0d3b3fa..0000000 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/FileService.java +++ /dev/null @@ -1,121 +0,0 @@ -package com.ruoyi.system.service; - -import cn.hutool.core.date.DateUtil; -import cn.hutool.core.img.Img; -import cn.hutool.core.lang.UUID; -import cn.hutool.core.util.StrUtil; - -import java.awt.Image; -import java.awt.image.BufferedImage; -import java.io.*; -import java.nio.ByteBuffer; -import java.util.Calendar; - -import cn.hutool.crypto.SecureUtil; -import cn.hutool.crypto.symmetric.SymmetricAlgorithm; -import cn.hutool.crypto.symmetric.SymmetricCrypto; -import com.ruoyi.common.utils.IdUtils; -import com.ruoyi.common.utils.spring.SpringUtils; -import org.springframework.web.multipart.MultipartFile; - -/** - *
- * - 本地文件保存服务
- * Author : J.L.Zhou
- * E-Mail : 2233875735@qq.com
- * Tel : 151 1104 7708
- * Date : 2021-6-1 11:48:16
- * Version : 1.0
- * Copyright 2021 jlzhou.top Inc. All rights reserved.
- * Warning: this content is only for internal circulation of the company.
- *          It is forbidden to divulge it or use it for other commercial purposes.
- * 
- */ -public interface FileService { - - - - /** - * - 保存文件,文件名随机 - * - * @param in - * @param filename - 原文件名 - * @param pre - 存放路径前缀 - * @return - 保存的文件URL - * @throws 400-499 - */ - default String save(InputStream in, String filename, String pre) { - return save(in, filename, pre, true); - } - - /** - * - * @param in - * @param filename - 原文件名 - * @param pre - 存放路径前缀 - * @param randomName - 是否随机文件名 - * @return - * @throws 400-499 - */ - default String save(InputStream in, String filename, String pre, boolean randomName) { - if (StrUtil.isNotBlank(pre) && !pre.matches("^[\\w\\/]+$")) { - throw new RuntimeException("存放路径前缀只能有单词字符组成"); - } - try { - if (in == null || in.available() == 0) { - throw new RuntimeException("读取上传文件长度错误"); - } - } catch (IOException e) { - throw new RuntimeException("读取上传文件长度错误", e); - } - if (StrUtil.isEmpty(filename)) { - throw new RuntimeException("文件名为空"); - } - filename = filename.replace(" ", ""); - String name = (StrUtil.isNotBlank(pre)?(pre+"/"):"") - + generateURI("{yyyy}/{MM}/{dd}/{id36}" + (randomName ? ".{ext}" : "/{filename}.{ext}"), filename); - - return save(in, name); - - } - - String save(InputStream in, String filename); - - /** - * - 删除文件 - * - * @param file 文件的URL - */ - void delete(String file); - - /** - * 获取保存的文件对象 - * @param file - * @return - */ - File getFile(String file); - - /** - *
-	 * - 根据存放规则和文件名生成存放的URI
-	 * - 支持
-	 * 	- {yyyy}/{MM}/{dd}/{HH}/{mm}/{ss} 年月日时分秒
-	 * 	- {UUID} 32位的唯一标志
-	 * 	- {id} int类型的唯一id,防止重复建议+年月日路径
-	 * 	- {id16} int类型的唯一id16进制表示,防止重复建议+年月日路径
-	 * 	- {id36} int类型的唯一id36进制表示,防止重复建议+年月日路径
-	 * 	- {filename} 文件基础名称
-	 * 	- {ext} 扩展名
-	 * 
- * - * @param rule - * @param filename - * @return - */ - default String generateURI(String rule, String filename) { - return SpringUtils.getBean(ISysOssService.class).generateURI(rule, filename); - } - - - -} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOssConfigService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOssConfigService.java deleted file mode 100644 index 80d874f..0000000 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOssConfigService.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.ruoyi.system.service; - -import com.ruoyi.common.core.domain.PageQuery; -import com.ruoyi.common.core.page.TableDataInfo; -import com.ruoyi.system.domain.bo.SysOssConfigBo; -import com.ruoyi.system.domain.vo.SysOssConfigVo; - -import java.util.Collection; - -/** - * 对象存储配置Service接口 - * - * @author Lion Li - * @author 孤舟烟雨 - * @date 2021-08-13 - */ -public interface ISysOssConfigService { - - /** - * 初始化OSS配置 - */ - void init(); - - /** - * 查询单个 - */ - SysOssConfigVo queryById(Long ossConfigId); - - /** - * 查询列表 - */ - TableDataInfo queryPageList(SysOssConfigBo bo, PageQuery pageQuery); - - - /** - * 根据新增业务对象插入对象存储配置 - * - * @param bo 对象存储配置新增业务对象 - * @return - */ - Boolean insertByBo(SysOssConfigBo bo); - - /** - * 根据编辑业务对象修改对象存储配置 - * - * @param bo 对象存储配置编辑业务对象 - * @return - */ - Boolean updateByBo(SysOssConfigBo bo); - - /** - * 校验并删除数据 - * - * @param ids 主键集合 - * @param isValid 是否校验,true-删除前校验,false-不校验 - * @return - */ - Boolean deleteWithValidByIds(Collection ids, Boolean isValid); - - /** - * 启用停用状态 - */ - int updateOssConfigStatus(SysOssConfigBo bo); - -} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOssService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOssService.java deleted file mode 100644 index aae7bd8..0000000 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOssService.java +++ /dev/null @@ -1,396 +0,0 @@ -package com.ruoyi.system.service; - -import cn.hutool.core.date.DateUtil; -import cn.hutool.core.img.Img; -import cn.hutool.core.lang.UUID; -import cn.hutool.core.util.HexUtil; -import cn.hutool.crypto.SecureUtil; -import cn.hutool.crypto.symmetric.SymmetricAlgorithm; -import cn.hutool.crypto.symmetric.SymmetricCrypto; -import com.ruoyi.common.core.domain.PageQuery; -import com.ruoyi.common.core.page.TableDataInfo; -import com.ruoyi.common.helper.DataPermissionHelper; -import com.ruoyi.common.utils.IdUtils; -import com.ruoyi.system.domain.SysOss; -import com.ruoyi.system.domain.bo.SysOssBo; -import com.ruoyi.system.domain.vo.SysOssVo; -import org.slf4j.LoggerFactory; -import org.springframework.web.multipart.MultipartFile; - -import javax.servlet.http.HttpServletResponse; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.ByteBuffer; -import java.util.Calendar; -import java.util.Collection; -import java.util.Date; -import java.util.List; -import java.util.function.Supplier; - -/** - * 文件上传 服务层 - * - * @author Lion Li - */ -public interface ISysOssService { - - /** - * 服务商 - */ - public static enum Service { - minio, qiniu, aliyun, qcloud, image, upload; - } - - String IMAGE_WEBP = "webp"; - - /** - * 默认路径前缀 - */ - String PRE_DEFAULT = "default"; - - /** - * 设置指定服务商 - * - * @param handle - */ - void setService(Service service, Runnable handle); - - public T setService(Service service, Supplier handle); - - /** - * 只保存,不存放记录 - * - * @param handle 处理执行方法 - */ - void ignore(Runnable handle); - - - /** - * 只保存,不存放记录 - * - * @param handle 处理执行方法 - */ - public T ignore(Supplier handle); - - - TableDataInfo queryPageList(SysOssBo sysOss, PageQuery pageQuery); - - List listByIds(Collection ossIds); - - SysOssVo getById(Long ossId); - - default SysOssVo upload(MultipartFile file) { - return upload(file, PRE_DEFAULT); - } - - /** - * @param file - * @param pre - 图片保存路径前缀 - * @return - */ - default SysOssVo upload(MultipartFile file, String pre) { - if (file == null || file.isEmpty()) { - throw new RuntimeException("文件不能为空"); - } - try { - return save(file.getInputStream(), file.getOriginalFilename(), file.getContentType(), pre); - } catch (IOException e) { - throw new RuntimeException(e.getMessage(), e); - } - } - - - /** - * 保存文件, rule:{yyyy}/{MM}/{dd}/{id36}.{ext} - * - * @param in 输入流 - * @param filename 文件名 - * @param contentType 文件类型 - * @param pre 路径前缀 - * @return - */ - default SysOssVo save(InputStream in, String filename, String contentType, String pre) { - return save(in, filename, contentType, pre, "{yyyy}/{MM}/{dd}/{id36}.{ext}"); - } - - /** - * 保存文件,保留文件名,rule:{yyyy}/{MM}/{dd}/{id36}/{filename}.{ext} - * - * @param in 输入流 - * @param filename 文件名 - * @param contentType 文件类型 - * @param pre 路径前缀 - * @return - */ - default SysOssVo saveFilename(InputStream in, String filename, String contentType, String pre) { - return save(in, filename, contentType, pre, "{yyyy}/{MM}/{dd}/{id36}/{filename}.{ext}"); - } - - /** - * 保存文件,存放路径规则rule: - *
-   * - 根据存放规则和文件名生成存放的URI
-   * - 支持
-   * 	- {yyyy}/{MM}/{dd}/{HH}/{mm}/{ss} 年月日时分秒
-   * 	- {UUID} 32位的唯一标志
-   * 	- {i} 自增id
-   * 	- {id} 当日int类型的唯一id,防止重复建议+年月日路径
-   * 	- {id16} 当日int类型的唯一id16进制表示,防止重复建议+年月日路径
-   * 	- {id36} 当日int类型的唯一id36进制表示,防止重复建议+年月日路径
-   * 	- {filename} 文件基础名称
-   * 	- {ext} 扩展名
-   * 
- * - * @param in 输入流 - * @param filename 文件名 - * @param contentType 文件类型 - * @param pre 路径前缀 - * @param rule 路径规则 - * @return - */ - SysOssVo save(InputStream in, String filename, String contentType, String pre, String rule); - - SysOssVo uploadImgs(MultipartFile file, String pre); - - default SysOssVo uploadImgs(MultipartFile file, String pre, int maxWidth, int maxHeight, BufferedImage watermark) { - if (file == null || file.isEmpty()) { - throw new RuntimeException("图片不能为空"); - } - try { - return uploadImgs(file.getInputStream(), pre, maxWidth, maxHeight, watermark); - } catch (IOException e) { - throw new RuntimeException(e.getMessage(), e); - } - } - - /** - * @param in - * @param pre - 图片保存路径前缀 - * @param maxWidth - 缩放到指定的宽 小于1表示不缩放 - * @param maxHeight - 缩放到指定的高 小于1表示不缩放 - * @param watermark - 水印图片 null表示不加水印 - * @return - */ - SysOssVo uploadImgs(InputStream in, String pre, int maxWidth, int maxHeight, - BufferedImage watermark); - - void download(Long ossId, HttpServletResponse response) throws IOException; - - void download(String url, Service service, HttpServletResponse response) throws IOException; - - InputStream download(Long ossId) throws IOException; - - InputStream download(SysOssVo sysOss) throws IOException; - - InputStream download(String url, Service service) throws IOException; - - Boolean deleteWithValidByIds(Collection ids, Boolean isValid); - - - default InputStream formatImage(MultipartFile file, int maxWidth, int maxHeight, - BufferedImage watermark) { - try { - if (file == null || file.isEmpty()) { - throw new RuntimeException("上传图片不能为空"); - } - if (!file.getContentType().startsWith("image/")) { - throw new RuntimeException("上传的文件不是图片"); - } - return formatImage(file.getInputStream(), maxWidth, maxHeight, watermark); - } catch (IOException e) { - throw new RuntimeException("处理图片错误", e); - } - } - - default InputStream formatImage(Image img, int maxWidth, int maxHeight, BufferedImage watermark) { - if (img == null) { - throw new RuntimeException("图片不能为空"); - } - return formatImage(Img.from(img), maxWidth, maxHeight, watermark); - } - - default InputStream formatImage(InputStream in, int maxWidth, int maxHeight, BufferedImage watermark) { - return formatImage(Img.from(in), maxWidth, maxHeight, watermark); - } - - default InputStream formatImage(Img img, int maxWidth, int maxHeight, BufferedImage watermark) { - try { - int w = img.getImg().getWidth(null); - int h = img.getImg().getHeight(null); - if (maxWidth > 0 && maxHeight > 0) { - if (w > maxWidth || h > maxHeight) { - int outWidth = 0; - int outHeight = 0; - outHeight = maxWidth * h / w; - if (outHeight > maxHeight) { - outHeight = maxHeight; - outWidth = outHeight * w / h; - } else { - outWidth = maxWidth; - } - img = img.scale(outWidth, outHeight); - w = outWidth; - h = outHeight; - - } - } - - - if (watermark != null) { - int ww = watermark.getWidth(null); - int wh = watermark.getHeight(null); - if (w > ww && h > wh) { - img = img.pressImage(watermark, 0, 0, 1f); - } - } - - ByteArrayOutputStream pout = new ByteArrayOutputStream(); - - img.setTargetImageType(IMAGE_WEBP).write(pout); - - ByteArrayInputStream pin = new ByteArrayInputStream(pout.toByteArray()); - pout.close(); - pout = null; - return pin; - } catch (Exception e) { - throw new RuntimeException("处理图片错误", e); - } - } - - - /** - *
-   * - 根据存放规则和文件名生成存放的URI
-   * - 支持
-   * 	- {yyyy}/{MM}/{dd}/{HH}/{mm}/{ss} 年月日时分秒
-   * 	- {UUID} 32位的唯一标志
-   *  - {i} 自增id
-   * 	- {id} 当日int类型的唯一id,防止重复建议+年月日路径
-   * 	- {id16} 当日int类型的唯一id16进制表示,防止重复建议+年月日路径
-   * 	- {id36} 当日int类型的唯一id36进制表示,防止重复建议+年月日路径
-   * 	- {filename} 文件基础名称
-   * 	- {ext} 扩展名
-   * 
- * - * @param rule - * @param filename - * @return - */ - default String generateURI(String rule, String filename) { - Calendar c = Calendar.getInstance(); - if (rule.contains("{yyyy}")) { - rule = rule.replace("{yyyy}", "" + c.get(Calendar.YEAR)); - } - if (rule.contains("{MM}")) { - rule = rule.replace("{MM}", String.format("%02d", c.get(Calendar.MONTH) + 1)); - } - if (rule.contains("{dd}")) { - rule = rule.replace("{dd}", String.format("%02d", c.get(Calendar.DATE))); - } - if (rule.contains("{HH}")) { - rule = rule.replace("{HH}", String.format("%02d", c.get(Calendar.HOUR_OF_DAY))); - } - if (rule.contains("{mm}")) { - rule = rule.replace("{mm}", String.format("%02d", c.get(Calendar.MINUTE))); - } - if (rule.contains("{ss}")) { - rule = rule.replace("{ss}", String.format("%02d", c.get(Calendar.SECOND))); - } - if (rule.contains("{UUID}")) { - rule = rule.replace("{UUID}", UUID.fastUUID().toString(true)); - } - if (rule.contains("{i}")) { - rule = rule.replace("{i}", IdUtils.nextId(Id.groupName).toString()); - } - if (rule.contains("{id}")) { - rule = rule.replace("{id}", Long.toString(id.nextId())); - } - if (rule.contains("{id16}")) { - rule = rule.replace("{id16}", Long.toString(id.nextId(), 16)); - } - if (rule.contains("{id36}")) { - rule = rule.replace("{id36}", Long.toString(id.nextId(), 36)); - } - if (rule.contains("{filename}")) { - String temp = null; - if (filename.contains(".")) { - temp = filename.substring(0, filename.lastIndexOf(".")); - } else { - temp = filename; - } - rule = rule.replace("{filename}", temp); - } - - if (rule.contains("{ext}")) { - String temp = null; - if (filename.contains(".")) { - temp = filename.substring(filename.lastIndexOf(".") + 1); - } else { - temp = ""; - } - rule = rule.replace("{ext}", temp.toLowerCase()); - } - return rule; - } - - Id id = new Id(); - - static class Id { - - private static final String groupName = "file:id"; - - private SymmetricCrypto crypto; - - private String today = DateUtil.today(); - - public synchronized Long nextId() { - if (crypto == null || !today.equals(DateUtil.today())) { - today = DateUtil.today(); - byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.DES.getValue(), today.getBytes()).getEncoded(); - crypto = new SymmetricCrypto(SymmetricAlgorithm.DES, key); - } - ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES); - buffer.putInt(IdUtils.nextDayId(groupName).intValue()); - return Math.abs(ByteBuffer.wrap(crypto.encrypt(buffer.array())).getLong()); - } - - } - -// public static void main(String[] args) { -// byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.DES.getValue(),DateUtil.today().getBytes()).getEncoded(); -// System.out.println(HexUtil.encodeHexStr(key)); -//// byte[] key = HexUtil.decodeHex("a359f3fe88445c192f20d573c80af163"); -// SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.DES, key); -// -// -// ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES); -// -// System.out.println(buffer.array().length); -// System.out.println(aes.encrypt(buffer.array()).length); -// buffer.clear(); -// buffer.putInt(1); -// System.out.println(ByteBuffer.wrap(aes.encrypt(buffer.array())).getLong()); -// buffer.clear(); -// buffer.putInt(2); -// System.out.println(ByteBuffer.wrap(aes.encrypt(buffer.array())).getLong()); -// buffer.clear(); -// buffer.putInt(3); -// System.out.println(ByteBuffer.wrap(aes.encrypt(buffer.array())).getLong()); -// } - - SysOssVo url(SysOssVo oss, int second); - - default SysOssVo url(SysOssVo oss) { - return url(oss, 120); - } - - String url(Service service,String url,int second); - - default String url(Service service,String url) { - return url(service,url, 120); - } -} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java index 21fdd61..cfd3a89 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java @@ -8,7 +8,6 @@ import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator; -import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.ruoyi.common.config.RuoYiConfig; import com.ruoyi.common.constant.CacheConstants; import com.ruoyi.common.constant.Constants; @@ -16,11 +15,9 @@ import com.ruoyi.common.core.domain.event.LogininforEvent; import com.ruoyi.common.core.domain.dto.RoleDTO; import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.core.domain.model.LoginUser; -import com.ruoyi.common.core.domain.model.XcxLoginUser; import com.ruoyi.common.enums.DeviceType; import com.ruoyi.common.enums.LoginType; import com.ruoyi.common.enums.UserStatus; -import com.ruoyi.common.enums.UserType; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.exception.user.CaptchaException; import com.ruoyi.common.exception.user.CaptchaExpireException; @@ -63,8 +60,6 @@ public class SysLoginService { private final IdentifierGenerator id; - private final ISysOssService ossService; - private final RuoYiConfig config; @@ -139,7 +134,7 @@ public class SysLoginService { } String url = null; try { - url = ossService.uploadImgs(avatar,"avatar",400,400,null).getUrl(); +// url = ossService.uploadImgs(avatar,"avatar",400,400,null).getUrl(); }catch (Exception e){ log.debug("保存头像失败",e); // throw new RuntimeException("保存头像失败",e); diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/FileServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/FileServiceImpl.java deleted file mode 100644 index 1470632..0000000 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/FileServiceImpl.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.ruoyi.system.service.impl; - -import cn.hutool.core.io.IoUtil; -import cn.hutool.core.util.StrUtil; -import com.ruoyi.common.config.RuoYiConfig; -import com.ruoyi.system.service.FileService; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - - -import java.io.File; -import java.io.FileOutputStream; -import java.io.InputStream; - -/** - *
- * - 文件存储的本地存储实现
- * Author : J.L.Zhou
- * E-Mail : 2233875735@qq.com
- * Tel : 151 1104 7708
- * Date : 2021-09-16 14:17
- * Version : 1.0
- * Copyright 2021 jlzhou.top Inc. All rights reserved.
- * Warning: this content is only for internal circulation of the company.
- *          It is forbidden to divulge it or use it for other commercial purposes.
- * 
- **/ -@Service("upload") -@RequiredArgsConstructor -@Slf4j -public class FileServiceImpl implements FileService { - - public final RuoYiConfig config; - - @Override - public void delete(String filename) { - if (StrUtil.isEmpty(filename)) { - throw new RuntimeException("文件名不能为空"); - } - try { - if (filename.startsWith(config.upload.pre)) { - filename = filename.substring(config.upload.pre.length()); - } - if (filename.indexOf("?") > -1) { - filename = filename.substring(0, filename.indexOf("?")); - } - File file = new File(config.upload.savePath, filename); - log.debug("file:"+file.exists()); - file.delete(); - log.info("删除:{}",file.getAbsolutePath()); - } catch (Exception e) { - throw new RuntimeException("删除错误", e); - } - } - - @Override - public File getFile(String filename) { - if (StrUtil.isEmpty(filename)) { - throw new RuntimeException("文件名不能为空"); - } - - if (filename.startsWith(config.upload.pre)) { - filename = filename.substring(config.upload.pre.length()); - } - if (filename.indexOf("?") > -1) { - filename = filename.substring(0, filename.indexOf("?")); - } - return new File(config.upload.savePath, filename); - } - - @Override - public String save(InputStream in, String filename) { - File file = new File(config.upload.savePath, filename); - file.getParentFile().mkdirs(); - try( - FileOutputStream out = new FileOutputStream(file); - ) { - IoUtil.copy(in, out); - return config.upload.pre + "/" + filename; - } catch (Exception e) { - throw new RuntimeException(e); - } - } -} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssConfigServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssConfigServiceImpl.java deleted file mode 100644 index 2e90e50..0000000 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssConfigServiceImpl.java +++ /dev/null @@ -1,170 +0,0 @@ -package com.ruoyi.system.service.impl; - -import cn.hutool.core.bean.BeanUtil; -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ObjectUtil; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; -import com.baomidou.mybatisplus.core.toolkit.Wrappers; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.ruoyi.common.constant.CacheNames; -import com.ruoyi.common.core.domain.PageQuery; -import com.ruoyi.common.core.page.TableDataInfo; -import com.ruoyi.common.exception.ServiceException; -import com.ruoyi.common.utils.JsonUtils; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.common.utils.redis.CacheUtils; -import com.ruoyi.common.utils.redis.RedisUtils; -import com.ruoyi.oss.constant.OssConstant; -import com.ruoyi.system.domain.SysOssConfig; -import com.ruoyi.system.domain.bo.SysOssConfigBo; -import com.ruoyi.system.domain.vo.SysOssConfigVo; -import com.ruoyi.system.mapper.SysOssConfigMapper; -import com.ruoyi.system.service.ISysOssConfigService; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.Collection; -import java.util.List; - -/** - * 对象存储配置Service业务层处理 - * - * @author Lion Li - * @author 孤舟烟雨 - * @date 2021-08-13 - */ -@Slf4j -@RequiredArgsConstructor -@Service -public class SysOssConfigServiceImpl implements ISysOssConfigService { - - private final SysOssConfigMapper baseMapper; - - /** - * 项目启动时,初始化参数到缓存,加载配置类 - */ - @Override - public void init() { - List list = baseMapper.selectList(); - // 加载OSS初始化配置 - for (SysOssConfig config : list) { - String configKey = config.getConfigKey(); - if ("0".equals(config.getStatus())) { - RedisUtils.setCacheObject(OssConstant.DEFAULT_CONFIG_KEY, configKey); - } - CacheUtils.put(CacheNames.SYS_OSS_CONFIG, config.getConfigKey(), JsonUtils.toJsonString(config)); - } - } - - @Override - public SysOssConfigVo queryById(Long ossConfigId) { - return baseMapper.selectVoById(ossConfigId); - } - - @Override - public TableDataInfo queryPageList(SysOssConfigBo bo, PageQuery pageQuery) { - LambdaQueryWrapper lqw = buildQueryWrapper(bo); - Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); - return TableDataInfo.build(result); - } - - - private LambdaQueryWrapper buildQueryWrapper(SysOssConfigBo bo) { - LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); - lqw.eq(StringUtils.isNotBlank(bo.getConfigKey()), SysOssConfig::getConfigKey, bo.getConfigKey()); - lqw.like(StringUtils.isNotBlank(bo.getBucketName()), SysOssConfig::getBucketName, bo.getBucketName()); - lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysOssConfig::getStatus, bo.getStatus()); - return lqw; - } - - @Override - public Boolean insertByBo(SysOssConfigBo bo) { - SysOssConfig config = BeanUtil.toBean(bo, SysOssConfig.class); - validEntityBeforeSave(config); - boolean flag = baseMapper.insert(config) > 0; - if (flag) { - CacheUtils.put(CacheNames.SYS_OSS_CONFIG, config.getConfigKey(), JsonUtils.toJsonString(config)); - } - return flag; - } - - @Override - public Boolean updateByBo(SysOssConfigBo bo) { - SysOssConfig config = BeanUtil.toBean(bo, SysOssConfig.class); - validEntityBeforeSave(config); - LambdaUpdateWrapper luw = new LambdaUpdateWrapper<>(); - luw.set(ObjectUtil.isNull(config.getPrefix()), SysOssConfig::getPrefix, ""); - luw.set(ObjectUtil.isNull(config.getRegion()), SysOssConfig::getRegion, ""); - luw.set(ObjectUtil.isNull(config.getExt1()), SysOssConfig::getExt1, ""); - luw.set(ObjectUtil.isNull(config.getRemark()), SysOssConfig::getRemark, ""); - luw.eq(SysOssConfig::getOssConfigId, config.getOssConfigId()); - boolean flag = baseMapper.update(config, luw) > 0; - if (flag) { - CacheUtils.put(CacheNames.SYS_OSS_CONFIG, config.getConfigKey(), JsonUtils.toJsonString(config)); - } - return flag; - } - - /** - * 保存前的数据校验 - */ - private void validEntityBeforeSave(SysOssConfig entity) { - if (StringUtils.isNotEmpty(entity.getConfigKey()) && !checkConfigKeyUnique(entity)) { - throw new ServiceException("操作配置'" + entity.getConfigKey() + "'失败, 配置key已存在!"); - } - } - - @Override - public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { - if (isValid) { - if (CollUtil.containsAny(ids, OssConstant.SYSTEM_DATA_IDS)) { - throw new ServiceException("系统内置, 不可删除!"); - } - } - List list = CollUtil.newArrayList(); - for (Long configId : ids) { - SysOssConfig config = baseMapper.selectById(configId); - list.add(config); - } - boolean flag = baseMapper.deleteBatchIds(ids) > 0; - if (flag) { - list.forEach(sysOssConfig -> - CacheUtils.evict(CacheNames.SYS_OSS_CONFIG, sysOssConfig.getConfigKey())); - } - return flag; - } - - /** - * 判断configKey是否唯一 - */ - private boolean checkConfigKeyUnique(SysOssConfig sysOssConfig) { - long ossConfigId = ObjectUtil.isNull(sysOssConfig.getOssConfigId()) ? -1L : sysOssConfig.getOssConfigId(); - SysOssConfig info = baseMapper.selectOne(new LambdaQueryWrapper() - .select(SysOssConfig::getOssConfigId, SysOssConfig::getConfigKey) - .eq(SysOssConfig::getConfigKey, sysOssConfig.getConfigKey())); - if (ObjectUtil.isNotNull(info) && info.getOssConfigId() != ossConfigId) { - return false; - } - return true; - } - - /** - * 启用禁用状态 - */ - @Override - @Transactional(rollbackFor = Exception.class) - public int updateOssConfigStatus(SysOssConfigBo bo) { - SysOssConfig sysOssConfig = BeanUtil.toBean(bo, SysOssConfig.class); - int row = baseMapper.update(null, new LambdaUpdateWrapper() - .set(SysOssConfig::getStatus, "1")); - row += baseMapper.updateById(sysOssConfig); - if (row > 0) { - RedisUtils.setCacheObject(OssConstant.DEFAULT_CONFIG_KEY, sysOssConfig.getConfigKey()); - } - return row; - } - -} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssServiceImpl.java deleted file mode 100644 index 9cb69b3..0000000 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssServiceImpl.java +++ /dev/null @@ -1,399 +0,0 @@ -package com.ruoyi.system.service.impl; - -import cn.hutool.core.convert.Convert; -import cn.hutool.core.io.IoUtil; -import cn.hutool.core.io.file.FileNameUtil; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.toolkit.Wrappers; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.ruoyi.common.constant.CacheNames; -import com.ruoyi.common.core.domain.PageQuery; -import com.ruoyi.common.core.page.TableDataInfo; -import com.ruoyi.common.core.service.OssService; -import com.ruoyi.common.exception.ServiceException; -import com.ruoyi.common.utils.BeanCopyUtils; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.common.utils.file.FileUtils; -import com.ruoyi.common.utils.spring.SpringUtils; -import com.ruoyi.oss.core.OssClient; -import com.ruoyi.oss.entity.UploadResult; -import com.ruoyi.oss.enumd.AccessPolicyType; -import com.ruoyi.oss.factory.OssFactory; -import com.ruoyi.system.domain.SysOss; -import com.ruoyi.system.domain.bo.SysOssBo; -import com.ruoyi.system.domain.vo.SysOssVo; -import com.ruoyi.system.mapper.SysOssMapper; -import com.ruoyi.system.service.FileService; -import com.ruoyi.system.service.ISysConfigService; -import com.ruoyi.system.service.ISysOssService; -import lombok.RequiredArgsConstructor; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.http.MediaType; -import org.springframework.stereotype.Service; -import org.springframework.web.multipart.MultipartFile; - -import javax.servlet.http.HttpServletResponse; -import java.awt.image.BufferedImage; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.*; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -/** - * 文件上传 服务层实现 - * - * @author Lion Li - */ -@RequiredArgsConstructor -@Service -public class SysOssServiceImpl implements ISysOssService, OssService { - - private static final String UPLOAD = "UPLOAD"; - private final ISysConfigService configService; - private final FileService fileService; - private final SysOssMapper baseMapper; - private static ThreadLocal IGNORE_THREAD_LOCAL = new ThreadLocal<>(); - private static ThreadLocal SERVICE_THREAD_LOCAL = new ThreadLocal<>(); - - @Override - public void setService(Service service, Runnable handle) { - SERVICE_THREAD_LOCAL.set(service); - try { - handle.run(); - } finally { - SERVICE_THREAD_LOCAL.remove(); - } - } - - @Override - public T setService(Service service, Supplier handle) { - SERVICE_THREAD_LOCAL.set(service); - try { - return handle.get(); - } finally { - SERVICE_THREAD_LOCAL.remove(); - } - } - - @Override - public void ignore(Runnable handle) { - IGNORE_THREAD_LOCAL.set(true); - try { - handle.run(); - } finally { - IGNORE_THREAD_LOCAL.remove(); - } - } - - @Override - public T ignore(Supplier handle) { - IGNORE_THREAD_LOCAL.set(true); - try { - return handle.get(); - } finally { - IGNORE_THREAD_LOCAL.remove(); - } - } - - @Override - public TableDataInfo queryPageList(SysOssBo bo, PageQuery pageQuery) { - LambdaQueryWrapper lqw = buildQueryWrapper(bo); - Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); - List filterResult = result.getRecords().stream().map(this::url).collect(Collectors.toList()); - result.setRecords(filterResult); - return TableDataInfo.build(result); - } - - @Override - public List listByIds(Collection ossIds) { - List list = new ArrayList<>(); - for (Long id : ossIds) { - SysOssVo vo = SpringUtils.getBean(ISysOssService.class).getById(id); - if (ObjectUtil.isNotNull(vo)) { - list.add(this.url(vo)); - } - } - return list; - } - - @Override - public String selectUrlByIds(String ossIds) { - List list = new ArrayList<>(); - for (Long id : StringUtils.splitTo(ossIds, Convert::toLong)) { - SysOssVo vo = SpringUtils.getBean(ISysOssService.class).getById(id); - if (ObjectUtil.isNotNull(vo)) { - list.add(this.url(vo).getUrl()); - } - } - return String.join(StringUtils.SEPARATOR, list); - } - - private LambdaQueryWrapper buildQueryWrapper(SysOssBo bo) { - Map params = bo.getParams(); - LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); - lqw.like(StringUtils.isNotBlank(bo.getFileName()), SysOss::getFileName, bo.getFileName()); - lqw.like(StringUtils.isNotBlank(bo.getOriginalName()), SysOss::getOriginalName, bo.getOriginalName()); - lqw.eq(StringUtils.isNotBlank(bo.getFileSuffix()), SysOss::getFileSuffix, bo.getFileSuffix()); - lqw.eq(StringUtils.isNotBlank(bo.getUrl()), SysOss::getUrl, bo.getUrl()); - lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null, - SysOss::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime")); - lqw.eq(StringUtils.isNotBlank(bo.getCreateBy()), SysOss::getCreateBy, bo.getCreateBy()); - lqw.eq(StringUtils.isNotBlank(bo.getService()), SysOss::getService, bo.getService()); - return lqw; - } - - @Cacheable(cacheNames = CacheNames.SYS_OSS, key = "#ossId") - @Override - public SysOssVo getById(Long ossId) { - return baseMapper.selectVoById(ossId); - } - - @Override - public void download(Long ossId, HttpServletResponse response) throws IOException { - SysOssVo sysOss = SpringUtils.getBean(ISysOssService.class).getById(ossId); - if (ObjectUtil.isNull(sysOss)) { - throw new ServiceException("文件数据不存在!"); - } - FileUtils.setAttachmentResponseHeader(response, sysOss.getOriginalName()); - response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8"); - if (UPLOAD.equals(sysOss.getService())) { - try (InputStream inputStream = new FileInputStream(fileService.getFile(sysOss.getUrl()))) { - int available = inputStream.available(); - IoUtil.copy(inputStream, response.getOutputStream()); - response.setContentLength(available); - } catch (Exception e) { - throw new ServiceException(e.getMessage()); - } - } else { - OssClient storage = OssFactory.instance(sysOss.getService()); - try (InputStream inputStream = storage.getObjectContent(sysOss.getUrl())) { - int available = inputStream.available(); - IoUtil.copy(inputStream, response.getOutputStream()); - response.setContentLength(available); - } catch (Exception e) { - throw new ServiceException(e.getMessage()); - } - } - } - - public void download(String url, Service service, HttpServletResponse response) throws IOException { - FileUtils.setAttachmentResponseHeader(response, FileNameUtil.getName(url)); - response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8"); - if (UPLOAD.equalsIgnoreCase(service.name())) { - try (InputStream inputStream = new FileInputStream(fileService.getFile(url))) { - int available = inputStream.available(); - IoUtil.copy(inputStream, response.getOutputStream()); - response.setContentLength(available); - } catch (Exception e) { - throw new ServiceException(e.getMessage()); - } - } else { - OssClient storage = OssFactory.instance(service.name()); - try (InputStream inputStream = storage.getObjectContent(url)) { - int available = inputStream.available(); - IoUtil.copy(inputStream, response.getOutputStream()); - response.setContentLength(available); - } catch (Exception e) { - throw new ServiceException(e.getMessage()); - } - } - } - - @Override - public InputStream download(Long ossId) throws IOException { - SysOssVo sysOss = SpringUtils.getBean(ISysOssService.class).getById(ossId); - return download(sysOss); - } - - @Override - public InputStream download(SysOssVo sysOss) throws IOException { - if (ObjectUtil.isNull(sysOss)) { - throw new ServiceException("文件数据不存在!"); - } - if (UPLOAD.equals(sysOss.getService())) { - return new FileInputStream(fileService.getFile(sysOss.getUrl())); - } else { - OssClient storage = OssFactory.instance(sysOss.getService()); - return storage.getObjectContent(sysOss.getUrl()); - } - } - - @Override - public InputStream download(String url, Service service) throws IOException { - if (UPLOAD.equalsIgnoreCase(service.name())) { - return new FileInputStream(fileService.getFile(url)); - } else { - OssClient storage = OssFactory.instance(service.name()); - return storage.getObjectContent(url); - } - } - - - @Override - public SysOssVo save(InputStream in, String originalFileName, String contentType, String pre, String rule) { - String suffix = StringUtils.substring(originalFileName, originalFileName.lastIndexOf("."), originalFileName.length()); - SysOss oss = new SysOss(); - oss.setOriginalName(originalFileName); - oss.setFileSuffix(suffix); - if (configService.selectOssEnabled()) { - OssClient storage = SERVICE_THREAD_LOCAL.get() == null ? OssFactory.instance() : OssFactory.instance(SERVICE_THREAD_LOCAL.get().name()); - UploadResult uploadResult; - try { - String path = ""; - if (StrUtil.isNotBlank(storage.getProperties().getPrefix())) { - path += storage.getProperties().getPrefix() + "/"; - - } - if (StrUtil.isBlank(pre)) { - pre = PRE_DEFAULT; - } - path += pre + "/" + generateURI(rule, originalFileName); - uploadResult = storage.upload(in, path, contentType); - } catch (Exception e) { - throw new RuntimeException(e.getMessage(), e); - } - // 保存文件信息 - - oss.setUrl(uploadResult.getUrl()); - oss.setFileName(uploadResult.getFilename()); - oss.setService(storage.getConfigKey()); - - } else { - try { - String url = fileService.save(in, originalFileName, pre); - oss.setUrl(url); - oss.setFileName(url); - oss.setService(UPLOAD); - } catch (Exception e) { - throw new RuntimeException("保存文件失败", e); - } - } - if (IGNORE_THREAD_LOCAL.get() == null) { - baseMapper.insert(oss); - } - - SysOssVo sysOssVo = new SysOssVo(); - BeanCopyUtils.copy(oss, sysOssVo); - return sysOssVo; - } - - public SysOssVo uploadImgs(MultipartFile file, String pre) { - return uploadImgs(file, pre, configService.selectImageMaxWidth(), configService.selectImageMaxHeight(), configService.getWatermark()); - } - - @Override - public SysOssVo uploadImgs(InputStream inputStream, String pre, int maxWidth, int maxHeight, BufferedImage watermark) { - String originalFileName = "temp.webp"; - String suffix = StringUtils.substring(originalFileName, originalFileName.lastIndexOf("."), originalFileName.length()); - SysOss oss = new SysOss(); - oss.setFileSuffix(suffix); - oss.setOriginalName(originalFileName); - if (configService.selectOssEnabled()) { - - OssClient storage = SERVICE_THREAD_LOCAL.get() == null ? OssFactory.instance() : OssFactory.instance(SERVICE_THREAD_LOCAL.get().name()); - UploadResult uploadResult; - - try ( - InputStream in = formatImage(inputStream, maxWidth, maxWidth, watermark) - ) { - String path = ""; - if (StrUtil.isNotBlank(storage.getProperties().getPrefix())) { - - path += storage.getProperties().getPrefix() + "/"; - - } - if (StrUtil.isNotBlank(pre)) { - path += pre + "/"; - } - path += generateURI("{yyyy}/{MM}/{dd}/{id36}.webp", "a.webp"); - - uploadResult = storage.upload(in, path, "image/webp"); - } catch (IOException e) { - throw new RuntimeException(e.getMessage(), e); - } - // 保存文件信息 - - oss.setUrl(uploadResult.getUrl()); - oss.setFileName(uploadResult.getFilename()); - oss.setService(storage.getConfigKey()); - } else { - try ( - InputStream in = formatImage(inputStream, maxWidth, maxWidth, watermark) - ) { - String url = fileService.save(in, originalFileName + ".webp", pre); - oss.setUrl(url); - oss.setFileName(url); - oss.setService(UPLOAD); - } catch (IOException e) { - throw new RuntimeException(e.getMessage(), e); - } - } - if (IGNORE_THREAD_LOCAL.get() == null) { - baseMapper.insert(oss); - } - SysOssVo sysOssVo = new SysOssVo(); - BeanCopyUtils.copy(oss, sysOssVo); - return sysOssVo; - } - - @Override - public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { - if (isValid) { - // 做一些业务上的校验,判断是否需要校验 - } - List list = baseMapper.selectBatchIds(ids); - for (SysOss sysOss : list) { - if (UPLOAD.equals(sysOss.getService())) { - fileService.delete(sysOss.getUrl()); - } else { - OssClient storage = OssFactory.instance(sysOss.getService()); - storage.delete(sysOss.getUrl()); - } - } - return baseMapper.deleteBatchIds(ids) > 0; - } - - @Override - public SysOssVo url(SysOssVo oss, int second) { - if (UPLOAD.equals(oss.getService())) { - return oss; - } else { - oss.setUrl(query(oss.getService(), oss.getUrl(), second)); - return oss; - } - } - - private String query(String service, String url, int second) { - OssClient storage = OssFactory.instance(service); - // 仅修改桶类型为 private 的URL,临时URL时长为120s - if (AccessPolicyType.PRIVATE == storage.getAccessPolicy()) { - String old = url; - if (url.indexOf("://") > -1) { - url = url.substring(url.indexOf("/", url.indexOf("//") + 2)); - } - if (url.startsWith("/")) { - url = url.substring(1); - } - url = url.substring(url.indexOf("/") + 1); - - String queryString = storage.getPrivateUrl(url, second); - queryString = queryString.substring(queryString.indexOf("?")); - return old + queryString; - } - return url; - } - - @Override - public String url(Service service, String url, int second) { - if (Service.upload.equals(service)) { - return url; - } else { - return query(service.name(), url, second); - } - } - -} diff --git a/ruoyi-system/src/main/resources/mapper/system/SysOssConfigMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysOssConfigMapper.xml deleted file mode 100644 index 77dc40e..0000000 --- a/ruoyi-system/src/main/resources/mapper/system/SysOssConfigMapper.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ruoyi-system/src/main/resources/mapper/system/SysOssMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysOssMapper.xml deleted file mode 100644 index a1e4ca8..0000000 --- a/ruoyi-system/src/main/resources/mapper/system/SysOssMapper.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/ruoyi.sql b/ruoyi.sql index d337b21..cea197e 100644 --- a/ruoyi.sql +++ b/ruoyi.sql @@ -11,7 +11,7 @@ Target Server Version : 100617 File Encoding : 65001 - Date: 11/10/2024 17:51:13 + Date: 24/10/2024 17:46:10 */ SET NAMES utf8mb4; @@ -209,6 +209,14 @@ CREATE TABLE `sys_logininfor` ( -- Records of sys_logininfor -- ---------------------------- INSERT INTO `sys_logininfor` VALUES (20241011000000001, 'admin', '127.0.0.1', '内网IP', 'MSEdge', 'Windows 10 or Windows Server 2016', '0', '登录成功', '2024-10-11 17:28:10'); +INSERT INTO `sys_logininfor` VALUES (20241012000000001, 'admin', '127.0.0.1', '内网IP', 'MSEdge', 'Windows 10 or Windows Server 2016', '0', '登录成功', '2024-10-12 17:19:10'); +INSERT INTO `sys_logininfor` VALUES (20241014000000001, 'admin', '127.0.0.1', '内网IP', 'MSEdge', 'Windows 10 or Windows Server 2016', '0', '登录成功', '2024-10-14 09:04:16'); +INSERT INTO `sys_logininfor` VALUES (20241022000000001, 'admin', '127.0.0.1', '内网IP', 'MSEdge', 'Windows 10 or Windows Server 2016', '0', '登录成功', '2024-10-22 10:58:47'); +INSERT INTO `sys_logininfor` VALUES (20241022000000002, 'admin', '127.0.0.1', '内网IP', 'MSEdge', 'Windows 10 or Windows Server 2016', '0', '登录成功', '2024-10-22 14:04:30'); +INSERT INTO `sys_logininfor` VALUES (20241023000000001, 'admin', '127.0.0.1', '内网IP', 'MSEdge', 'Windows 10 or Windows Server 2016', '0', '登录成功', '2024-10-23 09:03:22'); +INSERT INTO `sys_logininfor` VALUES (20241023000000002, 'admin', '127.0.0.1', '内网IP', 'MSEdge', 'Windows 10 or Windows Server 2016', '0', '登录成功', '2024-10-23 15:11:46'); +INSERT INTO `sys_logininfor` VALUES (20241024000000001, 'admin', '127.0.0.1', '内网IP', 'MSEdge', 'Windows 10 or Windows Server 2016', '0', '登录成功', '2024-10-24 08:49:02'); +INSERT INTO `sys_logininfor` VALUES (20241024000000002, 'admin', '127.0.0.1', '内网IP', 'MSEdge', 'Windows 10 or Windows Server 2016', '0', '登录成功', '2024-10-24 14:27:40'); -- ---------------------------- -- Table structure for sys_menu @@ -347,6 +355,7 @@ INSERT INTO `sys_menu` VALUES (2001, '定时任务查询', 2000, 1, '#', '', NUL INSERT INTO `sys_menu` VALUES (2002, '定时任务新增', 2000, 2, '#', '', NULL, 1, 0, 'F', '0', '0', 'sys:cron:add', '#', 'admin', '2024-08-08 10:02:52', '', NULL, ''); INSERT INTO `sys_menu` VALUES (2003, '定时任务修改', 2000, 3, '#', '', NULL, 1, 0, 'F', '0', '0', 'sys:cron:update', '#', 'admin', '2024-08-08 10:02:52', '', NULL, ''); INSERT INTO `sys_menu` VALUES (2004, '定时任务删除', 2000, 4, '#', '', NULL, 1, 0, 'F', '0', '0', 'sys:cron:remove', '#', 'admin', '2024-08-08 10:02:52', '', NULL, ''); +INSERT INTO `sys_menu` VALUES (20241022000000001, '文件图片上传', 5, 99, 'file', 'demo/file/index', NULL, 1, 1, 'C', '0', '0', 'demo:file:index', 'upload', 'admin', '2024-10-22 11:01:10', 'admin', '2024-10-22 11:01:10', ''); -- ---------------------------- -- Table structure for sys_notice @@ -426,63 +435,8 @@ INSERT INTO `sys_oper_log` VALUES (20241011000000016, '测试单表', 1, 'com.ru INSERT INTO `sys_oper_log` VALUES (20241011000000017, '测试单表', 1, 'com.ruoyi.demo.controller.TestDemoController.add()', 'POST', 1, 'admin', '', '/demo/demo', '127.0.0.1', '内网IP', '{\"createBy\":\"admin\",\"createTime\":\"2023-08-16 10:35:31\",\"id\":1,\"deptId\":102,\"userId\":4,\"orderNum\":1,\"testKey\":\"测试数据权限\",\"value\":\"测试\"}', '', 1, '\r\n### Error updating database. Cause: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry \'1\' for key \'PRIMARY\'\r\n### The error may exist in com/ruoyi/demo/mapper/TestDemoMapper.java (best guess)\r\n### The error may involve com.ruoyi.demo.mapper.TestDemoMapper.insert-Inline\r\n### The error occurred while setting parameters\r\n### SQL: INSERT INTO test_demo ( id, dept_id, user_id, create_user_id, login_ip, update_user_id, order_num, test_key, value, create_by, create_time, update_by, update_time ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )\r\n### Cause: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry \'1\' for key \'PRIMARY\'\n; Duplicate entry \'1\' for key \'PRIMARY\'; nested exception is java.sql.SQLIntegrityConstraintViolationException: Duplicate entry \'1\' for key \'PRIMARY\'', '2024-10-11 17:49:26'); INSERT INTO `sys_oper_log` VALUES (20241011000000018, '测试单表', 1, 'com.ruoyi.demo.controller.TestDemoController.add()', 'POST', 1, 'admin', '', '/demo/demo', '127.0.0.1', '内网IP', '{\"createBy\":\"admin\",\"createTime\":\"2023-08-16 10:35:31\",\"id\":1,\"deptId\":102,\"userId\":4,\"orderNum\":1,\"testKey\":\"测试数据权限\",\"value\":\"测试\"}', '', 1, '\r\n### Error updating database. Cause: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry \'1\' for key \'PRIMARY\'\r\n### The error may exist in com/ruoyi/demo/mapper/TestDemoMapper.java (best guess)\r\n### The error may involve com.ruoyi.demo.mapper.TestDemoMapper.insert-Inline\r\n### The error occurred while setting parameters\r\n### SQL: INSERT INTO test_demo ( id, dept_id, user_id, create_user_id, login_ip, update_user_id, order_num, test_key, value, create_by, create_time, update_by, update_time ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )\r\n### Cause: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry \'1\' for key \'PRIMARY\'\n; Duplicate entry \'1\' for key \'PRIMARY\'; nested exception is java.sql.SQLIntegrityConstraintViolationException: Duplicate entry \'1\' for key \'PRIMARY\'', '2024-10-11 17:49:28'); INSERT INTO `sys_oper_log` VALUES (20241011000000019, '测试单表', 2, 'com.ruoyi.demo.controller.TestDemoController.edit()', 'PUT', 1, 'admin', '', '/demo/demo', '127.0.0.1', '内网IP', '{\"createBy\":\"admin\",\"createTime\":\"2023-08-16 10:35:31\",\"id\":1,\"deptId\":102,\"userId\":4,\"orderNum\":1,\"testKey\":\"测试数据权限\",\"value\":\"测试\"}', '{\"code\":200,\"msg\":\"操作成功\"}', 0, '', '2024-10-11 17:50:05'); - --- ---------------------------- --- Table structure for sys_oss --- ---------------------------- -DROP TABLE IF EXISTS `sys_oss`; -CREATE TABLE `sys_oss` ( - `oss_id` bigint(20) NOT NULL COMMENT '对象存储主键', - `file_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '文件名', - `original_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '原名', - `file_suffix` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '文件后缀名', - `url` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'URL地址', - `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', - `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '上传人', - `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', - `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '更新人', - `service` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'minio' COMMENT '服务商', - PRIMARY KEY (`oss_id`) USING BTREE -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'OSS对象存储表' ROW_FORMAT = Compact; - --- ---------------------------- --- Records of sys_oss --- ---------------------------- - --- ---------------------------- --- Table structure for sys_oss_config --- ---------------------------- -DROP TABLE IF EXISTS `sys_oss_config`; -CREATE TABLE `sys_oss_config` ( - `oss_config_id` bigint(20) NOT NULL COMMENT '主建', - `config_key` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '配置key', - `access_key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT 'accessKey', - `secret_key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '秘钥', - `bucket_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '桶名称', - `prefix` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '前缀', - `endpoint` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '访问站点', - `domain` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '自定义域名', - `is_https` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'N' COMMENT '是否https(Y=是,N=否)', - `region` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '域', - `access_policy` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '1' COMMENT '桶权限类型(0=private 1=public 2=custom)', - `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '1' COMMENT '状态(0=正常,1=停用)', - `ext1` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '扩展字段', - `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '创建者', - `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', - `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '更新者', - `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', - `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', - PRIMARY KEY (`oss_config_id`) USING BTREE -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '对象存储配置表' ROW_FORMAT = Dynamic; - --- ---------------------------- --- Records of sys_oss_config --- ---------------------------- -INSERT INTO `sys_oss_config` VALUES (1, 'minio', 'base2024', 'base20241415926', 'files', '', '192.168.3.222:9000', '{root}', 'N', '', '1', '0', '', 'admin', '2023-04-28 11:22:31', 'admin', '2024-08-08 09:50:44', '正式环境中访问站点修改为minio:9000'); -INSERT INTO `sys_oss_config` VALUES (2, 'qiniu', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi', '', 's3-cn-north-1.qiniucs.com', '', 'N', '', '1', '1', '', 'admin', '2023-08-16 10:35:08', 'admin', '2023-08-16 10:35:08', NULL); -INSERT INTO `sys_oss_config` VALUES (3, 'aliyun', 'LTAI5tQMkJBHbYoDcBBrc1Kv', '25MWcjkWRlqTD0pSJrthqXe05CpjWS', 'test-data-resources', 'xxx', 'oss-cn-shenzhen.aliyuncs.com', '', 'Y', '', '1', '1', '', 'admin', '2023-08-16 10:35:08', 'admin', '2024-05-17 08:29:25', ''); -INSERT INTO `sys_oss_config` VALUES (4, 'qcloud', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi-1250000000', '', 'cos.ap-beijing.myqcloud.com', '', 'N', 'ap-beijing', '1', '1', '', 'admin', '2023-08-16 10:35:08', 'admin', '2023-08-16 10:35:08', NULL); -INSERT INTO `sys_oss_config` VALUES (5, 'image', 'ruoyi', 'ruoyi123', 'ruoyi', 'image', '127.0.0.1:9000', '', 'N', '', '1', '1', '', 'admin', '2023-08-16 10:35:08', 'admin', '2023-08-16 10:35:08', NULL); +INSERT INTO `sys_oper_log` VALUES (20241014000000020, '定时任务', 1, 'com.ruoyi.cron.api.CronTaskApi.add()', 'POST', 1, 'admin', '', '/system/cron/', '127.0.0.1', '内网IP', '{\"id\":202410140000001,\"taskId\":\"5553036b32681350546531d871d5edc9\",\"groupId\":0,\"enabled\":true,\"createTime\":\"2024-10-14 09:04\",\"paramELs\":[],\"userId\":1}', '', 0, '', '2024-10-14 09:04:34'); +INSERT INTO `sys_oper_log` VALUES (20241022000000021, '菜单管理', 1, 'com.ruoyi.web.controller.system.SysMenuController.add()', 'POST', 1, 'admin', '', '/system/menu', '127.0.0.1', '内网IP', '{\"createBy\":\"admin\",\"createTime\":\"2024-10-22 11:01:10\",\"updateBy\":\"admin\",\"updateTime\":\"2024-10-22 11:01:10\",\"parentId\":5,\"children\":[],\"menuId\":\"20241022000000001\",\"menuName\":\"文件图片上传\",\"orderNum\":99,\"path\":\"file\",\"component\":\"demo/file/index\",\"isFrame\":\"1\",\"isCache\":\"1\",\"menuType\":\"C\",\"visible\":\"0\",\"status\":\"0\",\"perms\":\"demo:file:index\",\"icon\":\"upload\"}', '{\"code\":200,\"msg\":\"操作成功\"}', 0, '', '2024-10-22 11:01:10'); -- ---------------------------- -- Table structure for sys_post @@ -677,7 +631,7 @@ CREATE TABLE `sys_user` ( -- ---------------------------- -- Records of sys_user -- ---------------------------- -INSERT INTO `sys_user` VALUES (1, 100, NULL, 'admin', '超级管理员', 'sys_user', 'admin@evolvecloud.cn', '13888888888', '1', '', '$2a$10$.ja7BDq5b8jxd6snbRvz8eAmg0loaDb05LR6SpR2F42huJb7GaOD6', '0', '0', '127.0.0.1', '2024-10-11 17:28:10', 'admin', '2024-01-03 10:35:07', 'admin', '2024-10-11 17:28:10', '管理员'); +INSERT INTO `sys_user` VALUES (1, 100, NULL, 'admin', '超级管理员', 'sys_user', 'admin@evolvecloud.cn', '13888888888', '1', '', '$2a$10$.ja7BDq5b8jxd6snbRvz8eAmg0loaDb05LR6SpR2F42huJb7GaOD6', '0', '0', '127.0.0.1', '2024-10-24 14:27:40', 'admin', '2024-01-03 10:35:07', 'admin', '2024-10-24 14:27:40', '管理员'); -- ---------------------------- -- Table structure for sys_user_post diff --git a/script/docker/nginx/conf/nginx.conf b/script/docker/nginx/conf/nginx.conf index 9bcc263..1ab66c5 100644 --- a/script/docker/nginx/conf/nginx.conf +++ b/script/docker/nginx/conf/nginx.conf @@ -33,14 +33,6 @@ http { } - upstream monitor-admin { - server 127.0.0.1:9090; - } - - upstream xxljob-admin { - server 127.0.0.1:9100; - } - server { listen 80; server_name localhost; @@ -68,10 +60,7 @@ http { # return 200 '{"msg":"演示模式,不允许操作","code":500}'; # } - # 限制外网访问内网 actuator 相关路径 - location ~ ^(/[^/]*)?/actuator(/.*)?$ { - return 403; - } + location / { root /usr/share/nginx/html;