diff --git a/base-admin/src/main/java/com/agri/web/controller/common/CommonController.java b/base-admin/src/main/java/com/agri/web/controller/common/CommonController.java index b26b209..70ad7b3 100644 --- a/base-admin/src/main/java/com/agri/web/controller/common/CommonController.java +++ b/base-admin/src/main/java/com/agri/web/controller/common/CommonController.java @@ -1,7 +1,13 @@ package com.agri.web.controller.common; +import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; + +import com.agri.common.config.OSSConfig; +import com.agri.common.utils.AliyunOSSUtils; +import com.agri.common.utils.ApiUtils; +import io.swagger.v3.oas.annotations.Operation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -31,6 +37,12 @@ public class CommonController @Autowired private ServerConfig serverConfig; + @Autowired + private AliyunOSSUtils aliyunOSSUtils; + + @Autowired + private OSSConfig ossConfig; + /** * 通用下载请求 * @@ -67,22 +79,25 @@ public class CommonController * 通用上传请求 */ @PostMapping("/common/upload") - public AjaxResult uploadFile(MultipartFile file) throws Exception - { - try - { + public AjaxResult uploadFile(MultipartFile file) throws Exception { + try { // 上传文件路径 String filePath = RuoYiConfig.getUploadPath(); - // 上传并返回新文件名称 - String fileName = FileUploadUtils.upload(filePath, file); - String url = serverConfig.getUrl() + fileName; + String url = ""; + String fileName = ""; + if ("oss".equals(ossConfig.getUploadType())){ + url = aliyunOSSUtils.upLoadFile(file); + fileName = FileUtils.getName(url); + }else { + // 上传并返回新文件名称 + fileName = FileUploadUtils.upload(filePath, file); + url = serverConfig.getUrl() + fileName; + } AjaxResult ajax = AjaxResult.success(); ajax.put("fileName", fileName); ajax.put("url", url); return ajax; - } - catch (Exception e) - { + } catch (Exception e) { return AjaxResult.error(e.getMessage()); } } @@ -115,4 +130,14 @@ public class CommonController log.error("下载文件失败", e); } } + + /** + * 获取上传文件签名 + * @return + */ + @Operation(summary = "获取上传文件签名",method = "GET") + @GetMapping("/getSign") + public String getOssSign() { + return ApiUtils.successData(aliyunOSSUtils.getSign()); + } } diff --git a/base-admin/src/main/resources/application-dev.yml b/base-admin/src/main/resources/application-dev.yml index 4ffb96e..8f6c7b5 100644 --- a/base-admin/src/main/resources/application-dev.yml +++ b/base-admin/src/main/resources/application-dev.yml @@ -226,3 +226,4 @@ sms: signature: 青蛙农业 #模板ID 非必须配置,如果使用sendMessage的快速发送需此配置 template-id: SMS_473805253 + diff --git a/base-admin/src/main/resources/application.yml b/base-admin/src/main/resources/application.yml index 9733594..2e321c2 100644 --- a/base-admin/src/main/resources/application.yml +++ b/base-admin/src/main/resources/application.yml @@ -13,3 +13,15 @@ knife4j: username: admin # 密码 password: admin123 + +# 阿里云相关配置 +aliyun: + oss: + endpoint: oss-cn-chengdu.aliyuncs.com + accessKeyId: LTAI5tEFj4BeiBfWgEDiRJn7 + accessKeySecret: 4kOMnSCvTN9KAufb1ul0dHzQuYo8Mz + bucketName: gov-cloud + prefixKey: backend/ + # domainName: https://ossnew.ljnhs.cn/ + maxSize: 100 #文件大小限制(单位: m) 默认10m + uploadType: oss diff --git a/base-common/pom.xml b/base-common/pom.xml index 0fd4b6f..1879b83 100644 --- a/base-common/pom.xml +++ b/base-common/pom.xml @@ -137,6 +137,19 @@ + + + + org.projectlombok + lombok + + + + + com.aliyun.oss + aliyun-sdk-oss + 3.10.2 + \ No newline at end of file diff --git a/base-common/src/main/java/com/agri/common/config/OSSConfig.java b/base-common/src/main/java/com/agri/common/config/OSSConfig.java new file mode 100644 index 0000000..91eb910 --- /dev/null +++ b/base-common/src/main/java/com/agri/common/config/OSSConfig.java @@ -0,0 +1,60 @@ +package com.agri.common.config; + +import com.aliyun.oss.OSS; +import com.aliyun.oss.OSSClientBuilder; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Component; + +@Component("ossConfig") +@ConfigurationProperties(prefix = "aliyun.oss") +@Data +public class OSSConfig { + /** + * OSS 访问域名 + */ + private String endpoint; + /** + * 标识用户 + */ + private String accessKeyId; + /** + * 加密签名字符串 + */ + private String accessKeySecret; + /** + * 存储空间名称 + */ + private String bucketName; + + /** + * 文件/对象名称的前缀,类似目录 + */ + private String prefixKey; + /** + * 自定义域名,预览图片用到 + */ +// private String domainName; + /** + * 文件大小限制(单位: m) + */ + private Long maxSize; + + /** + * 上传方式 + */ + private String uploadType; + + @Bean + public OSS ossClient() { + return new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret); + } + + public Long getMaxSize() { + if (maxSize == null || maxSize == 0) { + maxSize = 10L; + } + return maxSize * 1024 * 1204; + } +} diff --git a/base-common/src/main/java/com/agri/common/exception/BusinessException.java b/base-common/src/main/java/com/agri/common/exception/BusinessException.java new file mode 100644 index 0000000..269ccc3 --- /dev/null +++ b/base-common/src/main/java/com/agri/common/exception/BusinessException.java @@ -0,0 +1,47 @@ +package com.agri.common.exception; + +public class BusinessException extends RuntimeException { + + private String code; + private final String message; + + private Object data; + + public BusinessException(String code, String message) { + this.message = message; + this.code = code; + } + + public BusinessException(String code, String message, Object data) { + this.message = message; + this.code = code; + this.data = data; + } + + + public BusinessException(String message) { + this.message = message; + this.code = "0"; + } + + public String getCode() { + return code; + } + + @Override + public String getMessage() { + return message; + } + + public Object getData() { + return data; + } + + /** + * 去除异常中的堆栈跟踪信息,减小异常控制代码流性能损耗 + */ + // @Override + // public Throwable fillInStackTrace() { + // return this; + // } +} diff --git a/base-common/src/main/java/com/agri/common/utils/AliyunOSSUtils.java b/base-common/src/main/java/com/agri/common/utils/AliyunOSSUtils.java new file mode 100644 index 0000000..aeec577 --- /dev/null +++ b/base-common/src/main/java/com/agri/common/utils/AliyunOSSUtils.java @@ -0,0 +1,205 @@ +package com.agri.common.utils; + + +import com.agri.common.config.OSSConfig; +import com.agri.common.exception.BusinessException; +import com.aliyun.oss.OSS; +import com.aliyun.oss.common.utils.BinaryUtil; +import com.aliyun.oss.model.MatchMode; +import com.aliyun.oss.model.PolicyConditions; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.InputStream; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.UUID; + +@Slf4j +@Component +public class AliyunOSSUtils { + + /** + * 图片格式 + */ + private static final String[] FILE_TYPE = new String[]{".bmp", ".jpg", + ".jpeg", ".gif", ".png", ".mp4", ".jfif", ".ico", ".pdf"}; + + @Resource + private OSS ossClient; + + @Resource + private OSSConfig ossConfig; + + /** + * 图像上传 + * + * @param uploadFile 图像流处理 + * @return 文件访问路径 + */ + public String upLoadFile(MultipartFile uploadFile) { + return ossPutObject(uploadFile, null); + } + + /** + * 图像上传 + * + * @param uploadFile 图像流处理 + * @param prefixKey 文件/对象名称的前缀,类似目录,例如:backend/ + * @return 文件访问路径 + */ + public String upLoadFile(MultipartFile uploadFile, String prefixKey) { + return ossPutObject(uploadFile, prefixKey); + } + + @Deprecated + public String uploadAvatar(MultipartFile uploadFile) { + return ossPutObject(uploadFile,null); + } + + /** + * 图片上传 + * + * @param inputStream 图像流处理 + * @return 文件访问路径 + */ + @Deprecated + public String upLoadFile(InputStream inputStream) { + String suffix = ".png"; + //文件名 + String key = ossConfig.getPrefixKey() + UUID.randomUUID().toString() + .replace("-", "") + suffix; + try { + ossClient.putObject(ossConfig.getBucketName(), key, inputStream); + inputStream.close(); + log.info("--------图片上传成功--------"); + } catch (Exception e) { + log.info("oos上传失败:" + e.getMessage()); + throw new BusinessException("图片上传失败"); + } + //返回文件访问路径 + return key; + } + + /** + * 图片上传 + * + * @param inputStream 图像流处理 + * @return 文件访问路径 + */ + public String upLoadOtherFile(InputStream inputStream, String filename) { + String suffix = filename.substring(filename.lastIndexOf(".")); + //文件名 + String key = ossConfig.getPrefixKey() + UUID.randomUUID().toString() + .replace("-", "") + suffix; + try { + ossClient.putObject(ossConfig.getBucketName(), key, inputStream); + inputStream.close(); + } catch (Exception e) { + log.error("oos上传失败:{}", e.getMessage()); + throw new BusinessException("文件上传失败"); + } + //返回文件访问路径 + return key; + } + + /** + * 校验文件后缀 + * + * @param suffix 文件后缀 + * @return + */ + private void verifySuffix(String suffix) { + // 校验文件格式 + for (String type : FILE_TYPE) { + if (type.equalsIgnoreCase(suffix)) { + return; + } + } + throw new BusinessException("暂不支持此格式的文件"); + } + + /** + * 获取签名 + * + * @return + */ + public Map getSign() { + String accessId = ossConfig.getAccessKeyId(); // 请填写您的AccessKeyId。 + String endpoint = ossConfig.getEndpoint(); // 请填写您的 endpoint。 + String bucket = ossConfig.getBucketName(); // 请填写您的 bucketname 。 + String host = "https://" + bucket + "." + endpoint; // host的格式为 bucketname.endpoint + String dir = ossConfig.getPrefixKey(); // 用户上传文件时指定的前缀。 + + try { + long expireTime = 180;//60秒 + long expireEndTime = System.currentTimeMillis() + expireTime * 1000; + Date expiration = new Date(expireEndTime); + PolicyConditions policyConds = new PolicyConditions(); + policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000); + policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir); + + String postPolicy = ossClient.generatePostPolicy(expiration, policyConds); + byte[] binaryData = postPolicy.getBytes("utf-8"); + String encodedPolicy = BinaryUtil.toBase64String(binaryData); + String postSignature = ossClient.calculatePostSignature(postPolicy); + + Map respMap = new LinkedHashMap<>(); + respMap.put("accessid", accessId); + respMap.put("policy", encodedPolicy); + respMap.put("signature", postSignature); + respMap.put("dir", dir); + respMap.put("host", host); + respMap.put("expire", String.valueOf(expireEndTime / 1000)); + + return respMap; + + } catch (Exception e) { + // Assert.fail(e.getMessage()); + System.out.println(e.getMessage()); + throw new BusinessException("获取签名失败"); + } + } + + /** + * oss 文件上传并获得访问路径 + * + * @param uploadFile 需要上传的文件 + * @param prefixKey 文件/对象名称的前缀,类似目录 例如:backend/ + * @return + */ + private String ossPutObject(MultipartFile uploadFile, String prefixKey) { + + //获取文件名称 + String originalFilename = uploadFile.getOriginalFilename(); + //文件后缀 + String suffix = originalFilename.substring(originalFilename.lastIndexOf(".")); + verifySuffix(suffix); + long size = uploadFile.getSize(); + if (size > ossConfig.getMaxSize()) { + throw new BusinessException("文件超出大小限制"); + } + //前缀,类似目录 + if (StringUtils.isBlank(prefixKey)) { + prefixKey = ossConfig.getPrefixKey(); + } + //文件路径=prefixKey+文件名称 + String key = prefixKey + UUID.randomUUID().toString() + .replace("-", "") + suffix; + try { + InputStream inputStream = uploadFile.getInputStream(); + ossClient.putObject(ossConfig.getBucketName(), key, inputStream); + inputStream.close(); + log.info("--------文件上传成功--------"); + } catch (Exception e) { + log.error("oos上传失败:", e); + throw new BusinessException("图片上传失败"); + } + //返回文件访问路径 + return key; + } + +} diff --git a/base-common/src/main/java/com/agri/common/utils/ApiUtils.java b/base-common/src/main/java/com/agri/common/utils/ApiUtils.java new file mode 100644 index 0000000..6225682 --- /dev/null +++ b/base-common/src/main/java/com/agri/common/utils/ApiUtils.java @@ -0,0 +1,123 @@ +package com.agri.common.utils; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.serializer.SerializeConfig; +import com.alibaba.fastjson.serializer.SerializerFeature; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.HashMap; + +@Data +public class ApiUtils { + /** 成功code */ + public static final int SUCCESS_CODE = 200; + /** 成功消息 */ + public static final String SUCCESS_MESSAGE = "操作成功"; + /** 错误code */ + public static final int ERROR_CODE = 500; + /** 错误消息 */ + public static final String ERROR_MESSAGE = "操作失败"; + /** api登录失效 */ + public static final String LOGIN_FAILURE = "10001"; + /** 驴团长api登录失效 */ + public static final String GROUPBUY_LOGIN_FAILURE = "110001"; + /** 缺少服务商(企业)id参数 **/ + public static final String ENTERPRISEID_MISS = "10002"; + + /** 订单提示弹窗 **/ + public static final String ORDER_TIPS = "10003"; + /** 商品详情error显示占位图 */ + public static final String GOODS_DETAILS_ERROR = "10005"; + + /** 服务商品库存不足 */ + public static final String UNDER_STOCK = "10006"; + + /** 商品已存在 */ + public static final String GOODS_EXISTS = "10007"; + + /** 数据不存在 */ + public static final String DATA_NOT_EXISTS = "10008"; + + /** 硬弹框code */ + public static final String HARDFRAME = "33333"; + + /** 未注册弹窗 */ + public static final String UNRFGISTERED_TIPS = "12100"; + + /** 用户被禁用 */ + public static final String USER_DISABLED = "12101"; + + /** 城市合伙人权限未开通 */ + public static final String USER_AUTH_FAILURE = "12102"; + + /** 再来一单所有商品失效 */ + public static final String all_goods_effective = "44444"; + + private Integer code; + private String msg; + private Object data = new HashMap<>(); + + public ApiUtils() {} + + public ApiUtils(Object data) { + this.code = SUCCESS_CODE; + if(data!=null){ + this.data = data; + } + this.msg = SUCCESS_MESSAGE; + } + public ApiUtils(Integer code, String message) { + this.code = code; + this.msg = message; + } + public ApiUtils(Integer code, String message, Object data) { + this.code = code; + this.msg = message; + this.data = data; + } + + public static SerializeConfig config = new SerializeConfig(); + //对象为null返回{} + //枚举类型处理 + public static SerializerFeature[] reatures = new SerializerFeature[] { + SerializerFeature.WriteMapNullValue,//为null也返回 + SerializerFeature.WriteNullStringAsEmpty,//字符串为null,返回"" + SerializerFeature.WriteNullListAsEmpty,//数组/列表为null,返回[] + SerializerFeature.WriteNullNumberAsZero,//数值类型(Integer,Long)为null,返回0 + SerializerFeature.DisableCircularReferenceDetect,// 消除循环引用 + }; + + static { + config.put(Date.class, new SimpleDateFormatSerializer());//日期类型处理,为null返回"" + config.put(BigDecimal.class, new BigDecimalSerializer());//BigDecimal类型处理,为null返回"0" + config.setAsmEnable(false); + } + + public static String error() { + return JSON.toJSONString(new ApiUtils(ERROR_CODE, ERROR_MESSAGE.toString()), config, reatures); + } + public static String error(String message) { + return JSON.toJSONString(new ApiUtils(ERROR_CODE,message), config, reatures); + } + public static String error(Integer code, String message) { + return JSON.toJSONString(new ApiUtils(code,message), config, reatures); + } + public static String success() { + return JSON.toJSONString(new ApiUtils(SUCCESS_CODE, SUCCESS_MESSAGE.toString()), config, reatures); + } + public static String success(Integer code, String message){ + return JSON.toJSONString(new ApiUtils(code, message), config, reatures); + } + public static String success(String message) { + return JSON.toJSONString(new ApiUtils(SUCCESS_CODE, message), config, reatures); + } + public static String successData(Object data) { + return JSON.toJSONString(new ApiUtils(data), config, reatures); + } + public static String resultData(Integer code, String message, Object data) { + return JSON.toJSONString(new ApiUtils(code, message, data), config, reatures); + } +} + diff --git a/base-common/src/main/java/com/agri/common/utils/BigDecimalSerializer.java b/base-common/src/main/java/com/agri/common/utils/BigDecimalSerializer.java new file mode 100644 index 0000000..ec25b06 --- /dev/null +++ b/base-common/src/main/java/com/agri/common/utils/BigDecimalSerializer.java @@ -0,0 +1,19 @@ +package com.agri.common.utils; + +import com.alibaba.fastjson.serializer.JSONSerializer; +import com.alibaba.fastjson.serializer.ObjectSerializer; + +import java.io.IOException; +import java.lang.reflect.Type; + +public class BigDecimalSerializer implements ObjectSerializer { + + @Override + public void write(JSONSerializer serializer, Object object, Object o1, Type type, int i) throws IOException { + if(object == null) { + serializer.write("0.00"); + return ; + } + serializer.write(String.format("%.2f", object)); + } +} diff --git a/base-common/src/main/java/com/agri/common/utils/SimpleDateFormatSerializer.java b/base-common/src/main/java/com/agri/common/utils/SimpleDateFormatSerializer.java new file mode 100644 index 0000000..150c903 --- /dev/null +++ b/base-common/src/main/java/com/agri/common/utils/SimpleDateFormatSerializer.java @@ -0,0 +1,29 @@ +package com.agri.common.utils; + +import com.alibaba.fastjson.serializer.JSONSerializer; +import com.alibaba.fastjson.serializer.ObjectSerializer; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class SimpleDateFormatSerializer implements ObjectSerializer { + + private final String timePattern="yyyy-MM-dd HH:mm:ss"; + private final String datePattern="yyyy-MM-dd"; + + @Override + public void write(JSONSerializer serializer, Object object, Object o1, Type type, int i) throws IOException { + if (object == null) { + serializer.getWriter().write("\"\""); + return; + } + + Date date = (Date) object; + SimpleDateFormat format = new SimpleDateFormat(timePattern); + + String text = format.format(date); + serializer.write(text); + } +} diff --git a/base-common/src/main/java/com/agri/common/utils/file/FileUtils.java b/base-common/src/main/java/com/agri/common/utils/file/FileUtils.java index ad83d10..a51d31d 100644 --- a/base-common/src/main/java/com/agri/common/utils/file/FileUtils.java +++ b/base-common/src/main/java/com/agri/common/utils/file/FileUtils.java @@ -25,6 +25,12 @@ import com.agri.common.utils.uuid.IdUtils; */ public class FileUtils { + /** 字符常量:斜杠 {@code '/'} */ + public static final char SLASH = '/'; + + /** 字符常量:反斜杠 {@code '\\'} */ + public static final char BACKSLASH = '\\'; + public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+"; /** @@ -256,4 +262,55 @@ public class FileUtils } return strFileExtendName; } + + /** + * 返回文件名 + * + * @param filePath 文件 + * @return 文件名 + */ + public static String getName(String filePath) + { + if (null == filePath) + { + return null; + } + int len = filePath.length(); + if (0 == len) + { + return filePath; + } + if (isFileSeparator(filePath.charAt(len - 1))) + { + // 以分隔符结尾的去掉结尾分隔符 + len--; + } + + int begin = 0; + char c; + for (int i = len - 1; i > -1; i--) + { + c = filePath.charAt(i); + if (isFileSeparator(c)) + { + // 查找最后一个路径分隔符(/或者\) + begin = i + 1; + break; + } + } + + return filePath.substring(begin, len); + } + + /** + * 是否为Windows或者Linux(Unix)文件分隔符
+ * Windows平台下分隔符为\,Linux(Unix)为/ + * + * @param c 字符 + * @return 是否为Windows或者Linux(Unix)文件分隔符 + */ + public static boolean isFileSeparator(char c) + { + return SLASH == c || BACKSLASH == c; + } }