引言:为什么要使用minio
MinIO是对象存储服务,它基于Apache License 开源协议,兼容Amazon S3云存储接口。适合存储非结构化数据,如图片,音频,视频,日志等。对象文件最大可以达到5TB。类似于腾讯的oss或者阿里的对象存储平台,适用于解决分布式云存储。
1. MinIO的基础概念
术语 | 含义 |
---|---|
Object | 存储到MinIO的基本对象。如文件,字节流等 |
Bucket | 存储Object的逻辑空间,每个Bucket之间的数据时相互隔离的。对于用户而言,相当于存放文件的顶层文件夹。 |
Drive | 存储Object的磁盘。在MinIO启动时,以参数的方式传入。 |
Set | 一组Drive的集合。根据集群规模自动划分Set,每个Set中的Drive分布在不同位置。 - 一个对象存储在一个Set上。 - 一个集群划分成多个Set。 - 一个Set包含的Drive数量是固定的,默认由系统根据集群规模自动计算出。 - 一个Set中的Drive尽可能分布在不同的节点上。 |
EC | 纠删码(Erasure Code),保证高可靠。 - n 份原始数据,m份编码数据。 - 任意小于等于m份的数据丢失,以通过剩下的数据还原出来。 |
2. MinIO的优点
序号 | 优点 |
---|---|
1 | 部署简单,支持各种平台 |
2 | 海量存储,支持单个对象最大5TB |
3 | 兼容 Amazon S3接口 |
4 | 低冗余,磁盘损坏高容忍 |
5 | 读写性能优异 |
6 | 便宜不花钱适用于搭建自己的云存储服务器 |
3. MinIO实战开发
我这里是使用docker进行安装的你可以安装别的版本
编写docker命令:
docker run --name minio -p 9000:9000 -p 9001:9001
-e "MINIO_ROOT_USER=admin"
-e "MINIO_ROOT_PASSWORD=admin123"
-v /root/minio/data:/data
-d minio/minio server /data --console-address ":9001"
–name:容器名称
MINIO_ROOT_USER: 账号
MINIO_ROOT_PASSWORD: 密码
-d:在后台运行
-v: 挂在data文件在本地
成功启动就可以直接登录页面了
http://IP号:9001/
或者也可以编写docker-compose启动minio(这里推荐大家使用docker-compose的方式启动,现在的启动也是这么启动的)
version: '3.8'
services:minio:image: minio/miniohostname: "minio"ports:- "9090:9090" # api 端口- "9091:9091" # 控制台端口environment:MINIO_ACCESS_KEY: admin #管理后台用户名MINIO_SECRET_KEY: admin123 #管理后台密码,最小8个字符volumes:- ./data:/data #映射当前目录下的data目录至容器内/data目录- ./config:/root/.minio/ #映射配置目录command: server --console-address ':9091' --address ':9090' /data #指定容器中的目录 /dataprivileged: truerestart: always #设置开机自启动
我这里是启动的单机版的minio(这里我是访问的本地虚拟机的地址大家如果使用的话建议买服务器上部署一下)
这里我是拿docker-compose启动的
账号:admin
密码 admin123
申请 accessKey secretKey 的秘钥后面会用到
4.springboot集成minio
4.1编写pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.6</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.cwg</groupId><artifactId>pushNotification</artifactId><version>0.0.1-SNAPSHOT</version><name>pushNotification</name><description>pushNotification</description><properties><java.version>1.8</java.version><springfox.boot.starter.version>3.0.0</springfox.boot.starter.version><commons.io.version>2.11.0</commons.io.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId></dependency><!-- io常用工具类 --><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>${commons.io.version}</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency><!--swagger3--><dependency><groupId>io.springfox</groupId><artifactId>springfox-boot-starter</artifactId><version>${springfox.boot.starter.version}</version></dependency><dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.2.2</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.1</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>3.0.2</version><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>
4.2编写application.yml文件
#minio文件服务器配置(这里的9090是我上传的端口不是我访问控制页面的端口不一样)
minio.address=http://192.168.80.135:9090
#这里的minio的accessKey跟secretKey 是登录进去自己创建的
minio.accessKey=ZHbAyn7zxejVOz0Vtpk7
minio.secretKey=QDDV8YNKThIqthI1xaqIbIvvzO5GL0gsOdpljEeE
minio.bucketName=myfile
server.port=8081
server.servlet.context-path=/api
# 配置文件上传大小限制
spring.servlet.multipart.max-file-size=200MB
spring.servlet.multipart.max-request-size=200MB
编写配置minio的配置文件对桶进行操作
package com.cwg.config;import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.cwg.entity.AjaxResult;
import com.cwg.util.StringUtils;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.*;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Encoder;import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.rowset.serial.SerialException;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;@Data
@Component
@Slf4j
public class MinIOService {@Value("${minio.address}")private String address;@Value("${minio.accessKey}")private String accessKey;@Value("${minio.secretKey}")private String secretKey;@Value("${minio.bucketName}")private String bucketName;public MinioClient getMinioClient() {try {return MinioClient.builder().endpoint(address).credentials(accessKey,secretKey).build();} catch (Exception e) {e.printStackTrace();return null;}}/*** 检查存储桶是否存在** @param bucketName 存储桶名称* @return*/public boolean bucketExists(MinioClient minioClient, String bucketName) {boolean flag = false;try {flag = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());if (flag) {return true;}} catch (Exception e) {e.printStackTrace();return false;}return false;}// Upload '/home/user/Photos/asiaphotos.zip' as object name 'asiaphotos-2015.zip' to bucket// 'asiatrip'./*** 创建存储桶** @param bucketName 存储桶名称*/public boolean makeBucket(MinioClient minioClient, String bucketName) {try {boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());//存储桶不存在则创建存储桶if (!found) {// 如果不存在创建桶minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 上传文件** @param file 上传文件* @return 成功则返回文件名,失败返回空*/public AjaxResult uploadFile(MinioClient minioClient, MultipartFile file) throws SerialException {//创建存储桶boolean createFlag = makeBucket(minioClient, bucketName);//创建存储桶失败if (createFlag == false) {throw new SerialException("创建存储桶失败");}try {String originalFilename = file.getOriginalFilename();int pointIndex = originalFilename.lastIndexOf(".");//得到文件流InputStream inputStream = file.getInputStream();//保证文件不重名(并且没有特殊字符)String fileName = bucketName+ DateUtil.format(new Date(), "_yyyy-MM-dd-HH-mm-ss") + (pointIndex > -1 ? originalFilename.substring(pointIndex) : "");minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(fileName).contentType(ViewContentType.getContentType(fileName)).stream(inputStream,inputStream.available(),-1).build());inputStream.close();return AjaxResult.success(fileName);} catch (Exception e) {e.printStackTrace();return AjaxResult.error();}}/*** 下载文件** @param originalName 文件路径*/public InputStream downloadFile(MinioClient minioClient, String originalName, HttpServletResponse response) {try {// 2. 获取对象的InputStream,并保存为文件InputStream file = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(originalName).build());String filename = new String(originalName.getBytes("ISO8859-1"), StandardCharsets.UTF_8);if (StrUtil.isNotBlank(originalName)) {filename = originalName;}response.setHeader("Content-Disposition", "attachment;filename=" + filename);ServletOutputStream servletOutputStream = response.getOutputStream();int len;byte[] buffer = new byte[1024];while ((len = file.read(buffer)) > 0) {servletOutputStream.write(buffer, 0, len);}servletOutputStream.flush();file.close();servletOutputStream.close();return file;} catch (Exception e) {e.printStackTrace();return null;}}/*** 删除文件** @param fileName 文件路径* @return*/public boolean deleteFile(MinioClient minioClient, String fileName) {try {minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(fileName).build());} catch (Exception e) {e.printStackTrace();return false;}return true;}/*** Description 生成预览链接,最大7天有效期;* 如果想永久有效,在 minio 控制台设置仓库访问规则总几率* Create by Mr.Fang** @param object 文件名称* @param contentType 预览类型 image/gif", "image/jpeg", "image/jpg", "image/png", "application/pdf* @Params**//*** @description:获取文件预览接口* @date: 2022/8/18 9:44* @param: fileName: 文件名* @Param: bucketName: 桶名* @Param: previewExpiry: 预览到期时间(小时)* @return: java.lang.String**/public String getPreviewUrl(MinioClient minioClient,String fileName,Integer previewExpiry) {if (StringUtils.isNotBlank(fileName)) {bucketName = StringUtils.isNotBlank(bucketName) ? bucketName : this.bucketName;try {minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(fileName).build());if (null != previewExpiry){return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(bucketName).object(fileName).expiry(previewExpiry, TimeUnit.HOURS).build());}else {return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(bucketName).object(fileName).build());}} catch (Exception e) {e.printStackTrace();}}return null;}/*** 查询所有桶文件** @return*/public List<Bucket> getListBuckets(MinioClient minioClient) {try {return minioClient.listBuckets();} catch (Exception e) {e.printStackTrace();}return Collections.emptyList();}/*** 批量删除文件* @param objects 对象名称* @return boolean*/public List<DeleteError> deleteObject(MinioClient minioClient,List<String> objects) {List<DeleteError> deleteErrors = new ArrayList<>();List<DeleteObject> deleteObjects = objects.stream().map(value -> new DeleteObject(value)).collect(Collectors.toList());Iterable<Result<DeleteError>> results =minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(deleteObjects).build());try {for (Result<DeleteError> result : results) {DeleteError error = result.get();deleteErrors.add(error);}} catch (Exception e) {e.printStackTrace();}return deleteErrors;}/*** 获取单个桶中的所有文件对象名称** @param bucket 桶名称* @return {@link List}<{@link String}>*/public List<String> getBucketObjectName(MinioClient minioClient,String bucket) {boolean exsit = makeBucket(minioClient,bucket);if (exsit) {List<String> listObjetcName = new ArrayList<>();try {Iterable<Result<Item>> myObjects = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucket).build());for (Result<Item> result : myObjects) {Item item = result.get();listObjetcName.add(item.objectName());}return listObjetcName;} catch (Exception e) {e.printStackTrace();}}return null;}/*** 获取某个桶下某个对象的URL** @param objectName 对象名 (文件夹名 )* @return*/public String getBucketObject(MinioClient minioClient, String objectName) throws Exception {// 删除之前先检查`my-bucket`是否存在。boolean found = makeBucket(minioClient,bucketName);if (found) {final GetObjectResponse object = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());return object.object();}return "";}/*** @description: 批量下载* @param: filenames: 多个文件名称* @Param: zip: 压缩包名称* @Param: res: 响应对象* @return: void**/public void batchDownload(MinioClient minioClient,List<String> filenames, String zip, HttpServletResponse res,HttpServletRequest req){try {BucketExistsArgs bucketArgs = BucketExistsArgs.builder().bucket(bucketName).build();boolean bucketExists = minioClient.bucketExists(bucketArgs);BufferedOutputStream bos = null;res.reset();bos = new BufferedOutputStream(res.getOutputStream());ZipOutputStream out = new ZipOutputStream(bos);res.setHeader("Access-Control-Allow-Origin", "*");for (int i=0;i<filenames.size();i++) {GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(bucketName).object(filenames.get(i)).build();InputStream object = minioClient.getObject(objectArgs);byte buf[] = new byte[1024];int length = 0;res.setCharacterEncoding("utf-8");res.setContentType("application/force-download");// 设置强制下载不打开res.setHeader("Access-Control-Expose-Headers", "Content-Disposition");res.setHeader("Content-Disposition", "attachment;filename=" + filenameEncoding(zip,req) + ".zip");out.putNextEntry(new ZipEntry(filenames.get(i)));while ((length = object.read(buf)) > 0) {out.write(buf, 0, length);}}out.close();bos.close();} catch (Exception e) {e.printStackTrace();}}/*** 设置不同浏览器编码* @param filename 文件名称* @param request 请求对象*/public static String filenameEncoding(String filename, HttpServletRequest request) throws UnsupportedEncodingException {// 获得请求头中的User-AgentString agent = request.getHeader("User-Agent");// 根据不同的客户端进行不同的编码if (agent.contains("MSIE")) {// IE浏览器filename = URLEncoder.encode(filename, "utf-8");} else if (agent.contains("Firefox")) {// 火狐浏览器BASE64Encoder base64Encoder = new BASE64Encoder();filename = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes("utf-8")) + "?=";} else {// 其它浏览器filename = URLEncoder.encode(filename, "utf-8");}return filename;}
}
package com.cwg.config;import cn.hutool.core.util.StrUtil;public enum ViewContentType {DEFAULT("default","application/octet-stream"),JPG("jpg", "image/jpeg"),TIFF("tiff", "image/tiff"),GIF("gif", "image/gif"),JFIF("jfif", "image/jpeg"),PNG("png", "image/png"),TIF("tif", "image/tiff"),ICO("ico", "image/x-icon"),JPEG("jpeg", "image/jpeg"),WBMP("wbmp", "image/vnd.wap.wbmp"),FAX("fax", "image/fax"),NET("net", "image/pnetvue"),JPE("jpe", "image/jpeg"),RP("rp", "image/vnd.rn-realpix");private String prefix;private String type;public static String getContentType(String prefix){if(StrUtil.isEmpty(prefix)){return DEFAULT.getType();}prefix = prefix.substring(prefix.lastIndexOf(".") + 1);for (ViewContentType value : ViewContentType.values()) {if(prefix.equalsIgnoreCase(value.getPrefix())){return value.getType();}}return DEFAULT.getType();}ViewContentType(String prefix, String type) {this.prefix = prefix;this.type = type;}public String getPrefix() {return prefix;}public String getType() {return type;}
}
编写控制层controller
package com.cwg.controller;import com.cwg.config.MinIOService;
import com.cwg.entity.AjaxResult;
import io.minio.MinioClient;
import io.minio.messages.DeleteError;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.rowset.serial.SerialException;
import java.util.ArrayList;
import java.util.List;@RequestMapping("/file")
@RestController
public class FileController {@Autowiredprivate MinIOService minIOService;/*** 上传文件** @param file 文件* @return*/@PostMapping("/uploadFile")public AjaxResult uploadFile(@RequestBody MultipartFile file) throws SerialException {MinioClient minioClient = minIOService.getMinioClient();if (minioClient == null) {throw new SerialException("连接MinIO服务器失败");}return minIOService.uploadFile(minioClient, file);}/*** 下载文件** @param response 返回请求* @param fileName 文件路径* @return
*/@GetMapping("/downloadFile")public String downloadFile(HttpServletResponse response,@RequestParam String fileName) throws SerialException {MinioClient minioClient = minIOService.getMinioClient();if (minioClient == null) {throw new SerialException("连接MinIO服务器失败");}return minIOService.downloadFile(minioClient, fileName, response) != null ? "下载成功" : "下载失败";}/*** 删除文件** @param fileName 文件路径* @return*/@DeleteMapping("/deleteFile/{fileName}")public AjaxResult deleteFile(@PathVariable("fileName") String fileName) throws SerialException {MinioClient minioClient = minIOService.getMinioClient();if (minioClient == null) {throw new SerialException("连接MinIO服务器失败");}boolean flag = minIOService.deleteFile(minioClient, fileName);return flag == Boolean.TRUE ? AjaxResult.success("删除成功") : AjaxResult.success("删除失败");}/*** 文件预览*/@GetMapping("/prevpreviewFile/{fileName}")public AjaxResult previewFile(@PathVariable(value = "fileName",required = true) String fileName) throws SerialException {MinioClient minioClient = minIOService.getMinioClient();if (minioClient == null) {throw new SerialException("连接MinIO服务器失败");}Integer previewExpiry=1;String previewUrl = minIOService.getPreviewUrl(minioClient, fileName, previewExpiry);return AjaxResult.success(previewUrl);}/*** 获取某个桶下某个对象的URL* */@GetMapping("/getBucketObject/{fileName}")public AjaxResult getBucketObject(@PathVariable(value = "fileName",required = true) String fileName) throws Exception {MinioClient minioClient = minIOService.getMinioClient();if (minioClient == null) {throw new SerialException("连接MinIO服务器失败");}return AjaxResult.success( minIOService.getBucketObject(minioClient, fileName));}/*** 批量删除某个文件* */@PostMapping("/getRemoveObjects")public AjaxResult getRemoveObjects(@RequestBody ArrayList<String> objectNames) throws SerialException {MinioClient minioClient = minIOService.getMinioClient();if (minioClient == null) {throw new SerialException("连接MinIO服务器失败");}List<DeleteError> deleteErrors = minIOService.deleteObject(minioClient, objectNames);return AjaxResult.success(deleteErrors);}/*多文件上传**/@ApiOperation("多文件上传minio")@PostMapping("/uploadAllFile")public AjaxResult upload(@RequestBody MultipartFile[] files) throws Exception {MinioClient minioClient = minIOService.getMinioClient();if (minioClient == null) {throw new SerialException("连接MinIO服务器失败");}for (MultipartFile file : files) {minIOService.uploadFile(minioClient, file);}return AjaxResult.success();}@PostMapping("/batchDownload")public void batchDownload(@RequestBody() List<String> fileNames,HttpServletResponse res, HttpServletRequest req) throws SerialException {MinioClient minioClient = minIOService.getMinioClient();if (minioClient == null) {throw new SerialException("连接MinIO服务器失败");}minIOService.batchDownload(minioClient,fileNames,"1.zip",res,req);}
}
编写返回的请求
package com.cwg.entity;import com.cwg.constant.HttpStatus;
import com.cwg.util.StringUtils;import java.util.HashMap;/*** 操作消息提醒* * @author cwg*/
public class AjaxResult extends HashMap<String, Object>
{private static final long serialVersionUID = 1L;/** 状态码 */public static final String CODE_TAG = "code";/** 返回内容 */public static final String MSG_TAG = "msg";/** 数据对象 */public static final String DATA_TAG = "data";/*** 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。*/public AjaxResult(){}/*** 初始化一个新创建的 AjaxResult 对象* * @param code 状态码* @param msg 返回内容*/public AjaxResult(int code, String msg){super.put(CODE_TAG, code);super.put(MSG_TAG, msg);}/*** 初始化一个新创建的 AjaxResult 对象* * @param code 状态码* @param msg 返回内容* @param data 数据对象*/public AjaxResult(int code, String msg, Object data){super.put(CODE_TAG, code);super.put(MSG_TAG, msg);if (StringUtils.isNotNull(data)){super.put(DATA_TAG, data);}}/*** 返回成功消息* * @return 成功消息*/public static AjaxResult success(){return AjaxResult.success("操作成功");}/*** 返回成功数据* * @return 成功消息*/public static AjaxResult success(Object data){return AjaxResult.success("操作成功", data);}/*** 返回成功消息* * @param msg 返回内容* @return 成功消息*/public static AjaxResult success(String msg){return AjaxResult.success(msg, null);}/*** 返回成功消息* * @param msg 返回内容* @param data 数据对象* @return 成功消息*/public static AjaxResult success(String msg, Object data){return new AjaxResult(HttpStatus.SUCCESS, msg, data);}/*** 返回警告消息** @param msg 返回内容* @return 警告消息*/public static AjaxResult warn(String msg){return AjaxResult.warn(msg, null);}/*** 返回警告消息** @param msg 返回内容* @param data 数据对象* @return 警告消息*/public static AjaxResult warn(String msg, Object data){return new AjaxResult(HttpStatus.WARN, msg, data);}/*** 返回错误消息* * @return 错误消息*/public static AjaxResult error(){return AjaxResult.error("操作失败");}/*** 返回错误消息* * @param msg 返回内容* @return 错误消息*/public static AjaxResult error(String msg){return AjaxResult.error(msg, null);}/*** 返回错误消息* * @param msg 返回内容* @param data 数据对象* @return 错误消息*/public static AjaxResult error(String msg, Object data){return new AjaxResult(HttpStatus.ERROR, msg, data);}/*** 返回错误消息* * @param code 状态码* @param msg 返回内容* @return 错误消息*/public static AjaxResult error(int code, String msg){return new AjaxResult(code, msg, null);}/*** 方便链式调用** @param key 键* @param value 值* @return 数据对象*/@Overridepublic AjaxResult put(String key, Object value){super.put(key, value);return this;}
}
全局异常处理
package com.cwg.constant;/*** 缓存的key 常量* * @author cwg*/
public class CacheConstants
{/*** 登录用户 redis key*/public static final String LOGIN_TOKEN_KEY = "login_tokens:";/*** 验证码 redis key*/public static final String CAPTCHA_CODE_KEY = "captcha_codes:";/*** 参数管理 cache key*/public static final String SYS_CONFIG_KEY = "sys_config:";/*** 字典管理 cache key*/public static final String SYS_DICT_KEY = "sys_dict:";/*** 防重提交 redis key*/public static final String REPEAT_SUBMIT_KEY = "repeat_submit:";/*** 限流 redis key*/public static final String RATE_LIMIT_KEY = "rate_limit:";/*** 登录账户密码错误次数 redis key*/public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt:";
}
package com.cwg.constant;import cn.hutool.jwt.Claims;/*** 通用常量信息* * @author cwg*/
public class Constants
{/*** UTF-8 字符集*/public static final String UTF8 = "UTF-8";/*** GBK 字符集*/public static final String GBK = "GBK";/*** www主域*/public static final String WWW = "www.";/*** http请求*/public static final String HTTP = "http://";/*** https请求*/public static final String HTTPS = "https://";/*** 通用成功标识*/public static final String SUCCESS = "0";/*** 通用失败标识*/public static final String FAIL = "1";/*** 登录成功*/public static final String LOGIN_SUCCESS = "Success";/*** 注销*/public static final String LOGOUT = "Logout";/*** 注册*/public static final String REGISTER = "Register";/*** 登录失败*/public static final String LOGIN_FAIL = "Error";/*** 验证码有效期(分钟)*/public static final Integer CAPTCHA_EXPIRATION = 2;/*** 令牌*/public static final String TOKEN = "token";/*** 令牌前缀*/public static final String TOKEN_PREFIX = "Bearer ";/*** 令牌前缀*/public static final String LOGIN_USER_KEY = "login_user_key";/*** 用户ID*/public static final String JWT_USERID = "userid";/*** 用户头像*/public static final String JWT_AVATAR = "avatar";/*** 创建时间*/public static final String JWT_CREATED = "created";/*** 用户权限*/public static final String JWT_AUTHORITIES = "authorities";/*** 资源映射路径 前缀*/public static final String RESOURCE_PREFIX = "/profile";/*** RMI 远程方法调用*/public static final String LOOKUP_RMI = "rmi:";/*** LDAP 远程方法调用*/public static final String LOOKUP_LDAP = "ldap:";/*** LDAPS 远程方法调用*/public static final String LOOKUP_LDAPS = "ldaps:";/*** 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加)*/public static final String[] JOB_WHITELIST_STR = { "com.ruoyi" };/*** 定时任务违规的字符*/public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml","org.springframework", "org.apache", "com.ruoyi.common.utils.file", "com.ruoyi.common.config" };
}
package com.cwg.constant;/*** 代码生成通用常量* * @author cwg*/
public class GenConstants
{/** 单表(增删改查) */public static final String TPL_CRUD = "crud";/** 树表(增删改查) */public static final String TPL_TREE = "tree";/** 主子表(增删改查) */public static final String TPL_SUB = "sub";/** 树编码字段 */public static final String TREE_CODE = "treeCode";/** 树父编码字段 */public static final String TREE_PARENT_CODE = "treeParentCode";/** 树名称字段 */public static final String TREE_NAME = "treeName";/** 上级菜单ID字段 */public static final String PARENT_MENU_ID = "parentMenuId";/** 上级菜单名称字段 */public static final String PARENT_MENU_NAME = "parentMenuName";/** 数据库字符串类型 */public static final String[] COLUMNTYPE_STR = { "char", "varchar", "nvarchar", "varchar2" };/** 数据库文本类型 */public static final String[] COLUMNTYPE_TEXT = { "tinytext", "text", "mediumtext", "longtext" };/** 数据库时间类型 */public static final String[] COLUMNTYPE_TIME = { "datetime", "time", "date", "timestamp" };/** 数据库数字类型 */public static final String[] COLUMNTYPE_NUMBER = { "tinyint", "smallint", "mediumint", "int", "number", "integer","bit", "bigint", "float", "double", "decimal" };/** 页面不需要编辑字段 */public static final String[] COLUMNNAME_NOT_EDIT = { "id", "create_by", "create_time", "del_flag" };/** 页面不需要显示的列表字段 */public static final String[] COLUMNNAME_NOT_LIST = { "id", "create_by", "create_time", "del_flag", "update_by","update_time" };/** 页面不需要查询字段 */public static final String[] COLUMNNAME_NOT_QUERY = { "id", "create_by", "create_time", "del_flag", "update_by","update_time", "remark" };/** Entity基类字段 */public static final String[] BASE_ENTITY = { "createBy", "createTime", "updateBy", "updateTime", "remark" };/** Tree基类字段 */public static final String[] TREE_ENTITY = { "parentName", "parentId", "orderNum", "ancestors", "children" };/** 文本框 */public static final String HTML_INPUT = "input";/** 文本域 */public static final String HTML_TEXTAREA = "textarea";/** 下拉框 */public static final String HTML_SELECT = "select";/** 单选框 */public static final String HTML_RADIO = "radio";/** 复选框 */public static final String HTML_CHECKBOX = "checkbox";/** 日期控件 */public static final String HTML_DATETIME = "datetime";/** 图片上传控件 */public static final String HTML_IMAGE_UPLOAD = "imageUpload";/** 文件上传控件 */public static final String HTML_FILE_UPLOAD = "fileUpload";/** 富文本控件 */public static final String HTML_EDITOR = "editor";/** 字符串类型 */public static final String TYPE_STRING = "String";/** 整型 */public static final String TYPE_INTEGER = "Integer";/** 长整型 */public static final String TYPE_LONG = "Long";/** 浮点型 */public static final String TYPE_DOUBLE = "Double";/** 高精度计算类型 */public static final String TYPE_BIGDECIMAL = "BigDecimal";/** 时间类型 */public static final String TYPE_DATE = "Date";/** 模糊查询 */public static final String QUERY_LIKE = "LIKE";/** 相等查询 */public static final String QUERY_EQ = "EQ";/** 需要 */public static final String REQUIRE = "1";
}
package com.cwg.constant;/*** 返回状态码* * @author cwg*/
public class HttpStatus
{/*** 操作成功*/public static final int SUCCESS = 200;/*** 对象创建成功*/public static final int CREATED = 201;/*** 请求已经被接受*/public static final int ACCEPTED = 202;/*** 操作已经执行成功,但是没有返回数据*/public static final int NO_CONTENT = 204;/*** 资源已被移除*/public static final int MOVED_PERM = 301;/*** 重定向*/public static final int SEE_OTHER = 303;/*** 资源没有被修改*/public static final int NOT_MODIFIED = 304;/*** 参数列表错误(缺少,格式不匹配)*/public static final int BAD_REQUEST = 400;/*** 未授权*/public static final int UNAUTHORIZED = 401;/*** 访问受限,授权过期*/public static final int FORBIDDEN = 403;/*** 资源,服务未找到*/public static final int NOT_FOUND = 404;/*** 不允许的http方法*/public static final int BAD_METHOD = 405;/*** 资源冲突,或者资源被锁*/public static final int CONFLICT = 409;/*** 不支持的数据,媒体类型*/public static final int UNSUPPORTED_TYPE = 415;/*** 系统内部错误*/public static final int ERROR = 500;/*** 接口未实现*/public static final int NOT_IMPLEMENTED = 501;/*** 系统警告消息*/public static final int WARN = 601;
}
package com.cwg.constant;/*** 任务调度通用常量* * @author cwg*/
public class ScheduleConstants
{public static final String TASK_CLASS_NAME = "TASK_CLASS_NAME";/** 执行目标key */public static final String TASK_PROPERTIES = "TASK_PROPERTIES";/** 默认 */public static final String MISFIRE_DEFAULT = "0";/** 立即触发执行 */public static final String MISFIRE_IGNORE_MISFIRES = "1";/** 触发一次执行 */public static final String MISFIRE_FIRE_AND_PROCEED = "2";/** 不触发立即执行 */public static final String MISFIRE_DO_NOTHING = "3";public enum Status{/*** 正常*/NORMAL("0"),/*** 暂停*/PAUSE("1");private String value;private Status(String value){this.value = value;}public String getValue(){return value;}}
}
package com.cwg.constant;/*** 用户常量信息* * @author cwg*/
public class UserConstants
{/*** 平台内系统用户的唯一标志*/public static final String SYS_USER = "SYS_USER";/** 正常状态 */public static final String NORMAL = "0";/** 异常状态 */public static final String EXCEPTION = "1";/** 用户封禁状态 */public static final String USER_DISABLE = "1";/** 角色封禁状态 */public static final String ROLE_DISABLE = "1";/** 部门正常状态 */public static final String DEPT_NORMAL = "0";/** 部门停用状态 */public static final String DEPT_DISABLE = "1";/** 字典正常状态 */public static final String DICT_NORMAL = "0";/** 是否为系统默认(是) */public static final String YES = "Y";/** 是否菜单外链(是) */public static final String YES_FRAME = "0";/** 是否菜单外链(否) */public static final String NO_FRAME = "1";/** 菜单类型(目录) */public static final String TYPE_DIR = "M";/** 菜单类型(菜单) */public static final String TYPE_MENU = "C";/** 菜单类型(按钮) */public static final String TYPE_BUTTON = "F";/** Layout组件标识 */public final static String LAYOUT = "Layout";/** ParentView组件标识 */public final static String PARENT_VIEW = "ParentView";/** InnerLink组件标识 */public final static String INNER_LINK = "InnerLink";/** 校验是否唯一的返回标识 */public final static boolean UNIQUE = true;public final static boolean NOT_UNIQUE = false;/*** 用户名长度限制*/public static final int USERNAME_MIN_LENGTH = 2;public static final int USERNAME_MAX_LENGTH = 20;/*** 密码长度限制*/public static final int PASSWORD_MIN_LENGTH = 5;public static final int PASSWORD_MAX_LENGTH = 20;
}
package com.cwg.config.exception;import com.cwg.entity.AjaxResult;
import com.cwg.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;import javax.servlet.http.HttpServletRequest;
import java.nio.file.AccessDeniedException;/*** 全局异常处理器* * @author cwg*/
@RestControllerAdvice
public class GlobalExceptionHandler
{private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);/*** 请求方式不支持*/@ExceptionHandler(HttpRequestMethodNotSupportedException.class)public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,HttpServletRequest request){String requestURI = request.getRequestURI();log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod());return AjaxResult.error(e.getMessage());}/*** 业务异常*/@ExceptionHandler(ServiceException.class)public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request){log.error(e.getMessage(), e);Integer code = e.getCode();return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage());}/*** 系统异常*/@ExceptionHandler(Exception.class)public AjaxResult handleException(Exception e, HttpServletRequest request){String requestURI = request.getRequestURI();log.error("请求地址'{}',发生系统异常.", requestURI, e);return AjaxResult.error(e.getMessage());}
}