resultMap = new HashMap<>(4);
128 | // 页码
129 | resultMap.put("pageNo", pageNo);
130 | // 每页记录数量
131 | resultMap.put("pageSize", pageSize);
132 | // 总记录数
133 | resultMap.put("total", totalHitsCount);
134 | // 该页信息
135 | resultMap.put("items", list);
136 |
137 | return Result.ok(resultMap);
138 |
139 | }
140 |
141 | public String parseHighlightStr(String text, HighlightField field) {
142 | if (field != null) {
143 | Text[] fragments = field.getFragments();
144 | StringBuilder stringBuilder = new StringBuilder();
145 | for (Text str : fragments) {
146 | stringBuilder.append(str.string());
147 | }
148 | return stringBuilder.toString();
149 | } else {
150 | return text;
151 | }
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/elasticsearch-demo/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | server:
2 | # 服务端口
3 | port: 9999
4 | elasticsearch:
5 | # es访问ip
6 | hostname: 127.0.0.1
7 | # es访问port
8 | port: 9200
9 | blog:
10 | # 访问索引
11 | index: blog
12 | # 搜索返回字段
13 | source_fields: id,userId,title,introduce,createTime
--------------------------------------------------------------------------------
/elasticsearch-demo/src/test/java/com/fox/es/CommonTest.java:
--------------------------------------------------------------------------------
1 | package com.fox.es;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import com.fox.es.dto.BlogSimpleInfoDTO;
5 |
6 | /**
7 | * @author 狐狸半面添
8 | * @create 2023-03-23 1:04
9 | */
10 | public class CommonTest {
11 | public static void main(String[] args) {
12 | String json = "{\"createTime\":\"2023-03-23 00:40:20\",\"introduce\":\"Java的起源\",\"id\":1000,\"title\":\"Java语言\",\"userId\":1626989073847750657}";
13 | BlogSimpleInfoDTO blogSimpleInfoDTO = JSON.parseObject(json, BlogSimpleInfoDTO.class);
14 | System.out.println(blogSimpleInfoDTO);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/minio-chunk-demo/README.md:
--------------------------------------------------------------------------------
1 | 1.为了简单操作,这里只设计了一张表,即存放文件的表,放在了doc文件夹下,自行参考
2 |
3 | 2.代码的具体实现思路逻辑参考我的文章:[minio&前后端分离上传视频/上传大文件——前后端分离断点续传&minio分片上传实现](https://blog.csdn.net/qq_62982856/article/details/129002288)
4 |
5 | 3.MediaFileServiceImpl.java 中的uploadMergeChunks方法,里面有向数据库增加文件记录的操作,设置上传者id时我设置为了定值,实际开发中应当根据token拿到当前用户的Id。
--------------------------------------------------------------------------------
/minio-chunk-demo/doc/table.sql:
--------------------------------------------------------------------------------
1 | create database minio_demo;
2 |
3 | CREATE TABLE service_media_file
4 | (
5 | `id` BIGINT UNSIGNED PRIMARY KEY COMMENT '主键id(雪花算法)',
6 | `file_name` VARCHAR(255) NOT NULL COMMENT '文件名称',
7 | `file_type` CHAR(2) NOT NULL COMMENT '文件类型:文本,图片,音频,视频,其它',
8 | `file_format` VARCHAR(128) NOT NULL COMMENT '文件格式',
9 | `tag` VARCHAR(32) NOT NULL COMMENT '标签',
10 | `bucket` VARCHAR(32) NOT NULL COMMENT '存储桶',
11 | `file_path` VARCHAR(512) NOT NULL COMMENT '文件存储路径',
12 | `file_md5` CHAR(32) NOT NULL UNIQUE COMMENT '文件的md5值',
13 | `file_byte_size` BIGINT UNSIGNED NOT NULL COMMENT '文件的字节大小',
14 | `file_format_size` VARCHAR(24) NOT NULL COMMENT '文件的格式大小',
15 | `user_id` BIGINT NOT NULL COMMENT '上传人id',
16 | `create_time` DATETIME NOT NULL COMMENT '创建时间(上传时间)',
17 | `update_time` DATETIME NOT NULL COMMENT '修改时间'
18 | ) ENGINE = INNODB
19 | CHARACTER SET = utf8mb4 COMMENT '第三方服务-媒资文件表';
--------------------------------------------------------------------------------
/minio-chunk-demo/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.fox
8 | minio-chunk-demo
9 | 1.0-SNAPSHOT
10 |
11 |
12 |
13 | 8
14 | 8
15 | UTF-8
16 |
17 |
18 |
19 |
20 | org.springframework.boot
21 | spring-boot-starter-web
22 | 2.6.3
23 |
24 |
25 | org.projectlombok
26 | lombok
27 | 1.18.24
28 |
29 |
30 | com.baomidou
31 | mybatis-plus-boot-starter
32 | 3.5.2
33 |
34 |
35 |
36 | com.alibaba
37 | fastjson
38 | 1.2.33
39 |
40 |
41 | cn.hutool
42 | hutool-all
43 | 5.8.7
44 |
45 |
46 |
47 |
48 | mysql
49 | mysql-connector-java
50 | 8.0.30
51 |
52 |
53 |
54 | com.alibaba
55 | druid-spring-boot-starter
56 | 1.2.15
57 |
58 |
59 |
60 | org.aspectj
61 | aspectjweaver
62 | 1.9.7
63 |
64 |
65 |
66 |
67 | javax.validation
68 | validation-api
69 | 2.0.1.Final
70 |
71 |
72 |
73 |
74 | com.j256.simplemagic
75 | simplemagic
76 | 1.17
77 |
78 |
79 |
80 | io.minio
81 | minio
82 | 8.2.1
83 |
84 |
85 | me.tongfei
86 | progressbar
87 | 0.5.3
88 |
89 |
90 | com.squareup.okhttp3
91 | okhttp
92 | 4.8.1
93 |
94 |
95 |
96 | commons-codec
97 | commons-codec
98 | 1.13
99 |
100 |
101 |
102 | org.apache.tika
103 | tika-core
104 | 2.4.0
105 |
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/minio-chunk-demo/src/main/java/com/fox/miniodemo/MinioChunkDemoApplication.java:
--------------------------------------------------------------------------------
1 | package com.fox.miniodemo;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.context.annotation.EnableAspectJAutoProxy;
6 |
7 | /**
8 | * 启动类
9 | * EnableAspectJAutoProxy 暴露代理对象
10 | *
11 | * @author 狐狸半面添
12 | * @create 2023-01-16 17:03
13 | */
14 | @SpringBootApplication
15 | @EnableAspectJAutoProxy(exposeProxy = true)
16 | public class MinioChunkDemoApplication {
17 | public static void main(String[] args) {
18 | SpringApplication.run(MinioChunkDemoApplication.class, args);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/minio-chunk-demo/src/main/java/com/fox/miniodemo/config/MinioConfig.java:
--------------------------------------------------------------------------------
1 | package com.fox.miniodemo.config;
2 |
3 | import io.minio.MinioClient;
4 | import org.springframework.beans.factory.annotation.Value;
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.context.annotation.Configuration;
7 |
8 | /**
9 | * @author 狐狸半面添
10 | * @create 2023-02-08 16:26
11 | */
12 | @Configuration
13 | public class MinioConfig {
14 |
15 | /**
16 | * 连接的ip和端口
17 | */
18 | @Value("${minio.endpoint}")
19 | private String endpoint;
20 | /**
21 | * 访问秘钥(也称用户id)
22 | */
23 | @Value("${minio.accessKey}")
24 | private String accessKey;
25 | /**
26 | * 私有秘钥(也称密码)
27 | */
28 | @Value("${minio.secretKey}")
29 | private String secretKey;
30 |
31 | @Bean
32 | public MinioClient minioClient() {
33 | return MinioClient.builder()
34 | .endpoint(endpoint)
35 | .credentials(accessKey, secretKey)
36 | .build();
37 | }
38 | }
--------------------------------------------------------------------------------
/minio-chunk-demo/src/main/java/com/fox/miniodemo/config/MyBatisPlusConfig.java:
--------------------------------------------------------------------------------
1 | package com.fox.miniodemo.config;
2 |
3 | import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
4 | import com.fox.miniodemo.handler.MyMetaObjectHandler;
5 | import org.mybatis.spring.annotation.MapperScan;
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.context.annotation.Configuration;
8 | import org.springframework.transaction.annotation.EnableTransactionManagement;
9 |
10 | /**
11 | * 与ORM框架mybatis-plus相关的配置
12 | * EnableTransactionManagement 开启事务
13 | *
14 | * @author 狐狸半面添
15 | * @create 2023-01-15 22:40
16 | */
17 | @Configuration
18 | @EnableTransactionManagement
19 | @MapperScan("com.fox.miniodemo.dao")
20 | public class MyBatisPlusConfig {
21 | @Bean
22 | public MetaObjectHandler metaObjectHandler(){
23 | return new MyMetaObjectHandler();
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/minio-chunk-demo/src/main/java/com/fox/miniodemo/constant/HttpStatus.java:
--------------------------------------------------------------------------------
1 | package com.fox.miniodemo.constant;
2 |
3 | /**
4 | * Http状态码响应常量
5 | *
6 | * @author 狐狸半面添
7 | * @create 2023-01-17 16:46
8 | */
9 | public enum HttpStatus {
10 | /**
11 | * 请求处理成功
12 | */
13 | HTTP_OK(200,"成功"),
14 | /**
15 | * 请求报文语法错误或传入后端的参数错误(格式,范围等)
16 | */
17 | HTTP_BAD_REQUEST(400,"参数校验失败"),
18 | /**
19 | * 需要通过HTTP认证,或认证失败
20 | */
21 | HTTP_UNAUTHORIZED(401,"认证失败"),
22 | /**
23 | * 请求资源被拒绝,权限不足
24 | */
25 | HTTP_FORBIDDEN(403,"权限不足"),
26 | /**
27 | * 无法找到请求资源(服务器无理由拒绝)
28 | */
29 | HTTP_NOT_FOUND( 404,"无法找到请求资源"),
30 | /**
31 | * 服务器故障或Web应用故障
32 | */
33 | HTTP_INTERNAL_ERROR( 500,"服务器异常"),
34 | /**
35 | * 非法操作,对服务器的恶意请求或攻击
36 | */
37 | HTTP_ILLEGAL_OPERATION(700,"非法操作"),
38 | /**
39 | * 操作频繁,需要稍后再试
40 | */
41 | HTTP_TRY_AGAIN_LATER (701,"频繁操作,请稍后再试"),
42 | /**
43 | * 登录过期,需要重新登录
44 | */
45 | HTTP_LOGIN_EXPIRE (702,"登录过期,请重新登录"),
46 | /**
47 | * 账号在其它地方登录,强制下线
48 | */
49 | HTTP_USER_CROWDING(703,"账号在其它地方登录,您已被强制下线"),
50 |
51 | /**
52 | * 校验失败
53 | */
54 | HTTP_VERIFY_FAIL(704,"验证失败"),
55 |
56 | /**
57 | * 未查询到相关信息
58 | */
59 | HTTP_INFO_NOT_EXIST(705,"信息不存在"),
60 |
61 | /**
62 | * 信息被拒绝获取
63 | */
64 | HTTP_INFO_REFUSE(706,"被拒绝获取"),
65 | /**
66 | * 操作重复,该操作之前已生效
67 | */
68 | HTTP_REPEAT_SUCCESS_OPERATE(707,"重复已生效的操作");
69 |
70 | private final int code;
71 | private final String value;
72 |
73 | private HttpStatus(int code, String value) {
74 | this.code = code;
75 | this.value = value;
76 | }
77 |
78 | public int getCode() {
79 | return code;
80 | }
81 |
82 | public String getValue() {
83 | return value;
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/minio-chunk-demo/src/main/java/com/fox/miniodemo/controller/MediaFileController.java:
--------------------------------------------------------------------------------
1 | package com.fox.miniodemo.controller;
2 |
3 |
4 | import com.alibaba.fastjson.JSONObject;
5 | import com.fox.miniodemo.entity.Result;
6 | import com.fox.miniodemo.service.MediaFileService;
7 | import com.fox.miniodemo.vo.CheckChunkFileVO;
8 | import com.fox.miniodemo.vo.UploadMergeChunksVO;
9 | import org.springframework.validation.annotation.Validated;
10 | import org.springframework.web.bind.annotation.*;
11 | import org.springframework.web.multipart.MultipartFile;
12 |
13 | import javax.annotation.Resource;
14 |
15 | /**
16 | *
17 | * 第三方服务-媒资文件表 前端控制器
18 | *
19 | *
20 | * @author 狐狸半面添
21 | * @since 2023-02-08
22 | */
23 | @RestController
24 | @RequestMapping("/media-file")
25 | public class MediaFileController {
26 | @Resource
27 | private MediaFileService mediaFileService;
28 |
29 |
30 | /**
31 | * 文件上传前检查文件是否已存在
32 | *
33 | * @param object 需要上传的文件的md5值
34 | * @return 是否存在, false-不存在 true-存在。如果存在,则会返回文件信息。
35 | */
36 | @PostMapping("/upload/checkFile")
37 | public Result checkFile(@RequestBody JSONObject object) {
38 | return mediaFileService.checkFile(object.getString("fileMd5"));
39 | }
40 |
41 | /**
42 | * 分块文件上传前检测分块文件是否已存在
43 | *
44 | * @param checkChunkFileVO 分块文件的源文件md5和该文件索引
45 | * @return 是否存在, false-不存在 true-存在
46 | */
47 | @PostMapping("/upload/checkChunk")
48 | public Result checkChunk(@Validated @RequestBody CheckChunkFileVO checkChunkFileVO) {
49 | return mediaFileService.checkChunk(checkChunkFileVO.getFileMd5(), checkChunkFileVO.getChunkIndex());
50 | }
51 |
52 | /**
53 | * 上传分块文件
54 | *
55 | * @param file 分块文件
56 | * @param fileMd5 原文件md5值
57 | * @param chunkIndex 分块文件索引
58 | * @return 上传情况
59 | */
60 | @PostMapping("/upload/uploadChunk")
61 | public Result uploadChunk(@RequestParam("file") MultipartFile file,
62 | @RequestParam("fileMd5") String fileMd5,
63 | @RequestParam("chunkIndex") Integer chunkIndex) throws Exception {
64 | return mediaFileService.uploadChunk(fileMd5, chunkIndex, file.getBytes());
65 | }
66 |
67 | /**
68 | * 合并分块文件
69 | *
70 | * @param uploadMergeChunksVO 文件的md5十六进制值+文件名+文件标签+分块文件总数
71 | * @return 合并与上传情况,如果成功,则返回文件信息
72 | */
73 | @PostMapping("/upload/uploadMergeChunks")
74 | public Result uploadMergeChunks(@Validated @RequestBody UploadMergeChunksVO uploadMergeChunksVO) {
75 |
76 | return mediaFileService.uploadMergeChunks(
77 | uploadMergeChunksVO.getFileMd5(),
78 | uploadMergeChunksVO.getFileName(),
79 | uploadMergeChunksVO.getTag(),
80 | uploadMergeChunksVO.getChunkTotal()
81 | );
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/minio-chunk-demo/src/main/java/com/fox/miniodemo/dao/MediaFileMapper.java:
--------------------------------------------------------------------------------
1 | package com.fox.miniodemo.dao;
2 |
3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper;
4 | import com.fox.miniodemo.po.MediaFile;
5 |
6 | /**
7 | *
8 | * 第三方服务-媒资文件表 Mapper 接口
9 | *
10 | *
11 | * @author 狐狸半面添
12 | * @since 2023-02-08
13 | */
14 | public interface MediaFileMapper extends BaseMapper {
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/minio-chunk-demo/src/main/java/com/fox/miniodemo/entity/Result.java:
--------------------------------------------------------------------------------
1 | package com.fox.miniodemo.entity;
2 |
3 | import com.fasterxml.jackson.annotation.JsonInclude;
4 | import lombok.Getter;
5 | import lombok.NoArgsConstructor;
6 | import lombok.Setter;
7 |
8 | import java.io.Serializable;
9 |
10 | import static com.fox.miniodemo.constant.HttpStatus.HTTP_INTERNAL_ERROR;
11 | import static com.fox.miniodemo.constant.HttpStatus.HTTP_OK;
12 |
13 |
14 | /**
15 | * 返回给前端的统一工具类
16 | *
17 | * @author 狐狸半面添
18 | * @create 2023-01-16 19:19
19 | */
20 | @Getter
21 | @Setter
22 | @JsonInclude(JsonInclude.Include.NON_EMPTY)
23 | public class Result implements Serializable {
24 | private static final long serialVersionUID = 1L;
25 |
26 | /**
27 | * 状态码
28 | */
29 | private int code;
30 | /**
31 | * 信息
32 | */
33 | private String msg;
34 | /**
35 | * 数据
36 | */
37 | private Object data;
38 |
39 | private Result(int code, String msg) {
40 | this.code = code;
41 | this.msg = msg;
42 | }
43 |
44 | private Result(int code, String msg, Object data) {
45 | this.code = code;
46 | this.msg = msg;
47 | this.data = data;
48 |
49 | }
50 | private Result(){}
51 |
52 | public static Result ok() {
53 | return new Result(HTTP_OK.getCode(), HTTP_OK.getValue());
54 | }
55 |
56 |
57 | public static Result ok(Object data) {
58 | return new Result(HTTP_OK.getCode(), HTTP_OK.getValue(), data);
59 | }
60 |
61 | public static Result ok(String msg, Object data) {
62 | return new Result(HTTP_OK.getCode(), msg, data);
63 | }
64 |
65 | public static Result error() {
66 | return new Result(HTTP_INTERNAL_ERROR.getCode(), HTTP_INTERNAL_ERROR.getValue());
67 | }
68 |
69 | public static Result error(String msg) {
70 | return new Result(HTTP_INTERNAL_ERROR.getCode(), msg);
71 | }
72 |
73 | public static Result error(Integer code, String msg) {
74 | return new Result(code, msg);
75 | }
76 | public static Result error(String msg, Object data) {
77 | return new Result(HTTP_INTERNAL_ERROR.getCode(), msg, data);
78 | }
79 |
80 | public static Result error(int code, String msg, Object data) {
81 | return new Result(code, msg, data);
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/minio-chunk-demo/src/main/java/com/fox/miniodemo/handler/MyMetaObjectHandler.java:
--------------------------------------------------------------------------------
1 | package com.fox.miniodemo.handler;
2 |
3 | import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
4 | import org.apache.ibatis.reflection.MetaObject;
5 |
6 | import java.time.LocalDateTime;
7 |
8 | /**
9 | * mybatis-plus 自动填充器
10 | *
11 | * @author 狐狸半面添
12 | * @create 2023-01-18 23:14
13 | */
14 |
15 | public class MyMetaObjectHandler implements MetaObjectHandler {
16 |
17 | @Override
18 | public void insertFill(MetaObject metaObject) {
19 | this.setFieldValByName("createTime", LocalDateTime.now(), metaObject);
20 | this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject);
21 | }
22 |
23 | @Override
24 | public void updateFill(MetaObject metaObject) {
25 | this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/minio-chunk-demo/src/main/java/com/fox/miniodemo/po/MediaFile.java:
--------------------------------------------------------------------------------
1 | package com.fox.miniodemo.po;
2 |
3 | import com.baomidou.mybatisplus.annotation.*;
4 | import lombok.AllArgsConstructor;
5 | import lombok.Data;
6 | import lombok.NoArgsConstructor;
7 |
8 | import java.io.Serializable;
9 | import java.time.LocalDateTime;
10 |
11 | /**
12 | *
13 | * 第三方服务-媒资文件表
14 | *
15 | *
16 | * @author 狐狸半面添
17 | * @since 2023-02-08
18 | */
19 | @TableName("service_media_file")
20 | @Data
21 | @AllArgsConstructor
22 | @NoArgsConstructor
23 | public class MediaFile implements Serializable {
24 |
25 | private static final long serialVersionUID = 1L;
26 |
27 | /**
28 | * 主键id(雪花算法)
29 | */
30 | @TableId(value = "id", type = IdType.ASSIGN_ID)
31 | private Long id;
32 |
33 | /**
34 | * 文件名称
35 | */
36 | private String fileName;
37 |
38 | /**
39 | * 文件类型:文本,图片,音频,视频,其它
40 | */
41 | private String fileType;
42 |
43 | /**
44 | * 文件格式
45 | */
46 | private String fileFormat;
47 |
48 | /**
49 | * 标签
50 | */
51 | private String tag;
52 |
53 | /**
54 | * 存储桶
55 | */
56 | private String bucket;
57 |
58 | /**
59 | * 文件存储路径
60 | */
61 | private String filePath;
62 |
63 | /**
64 | * 文件的md5值
65 | */
66 | private String fileMd5;
67 |
68 | /**
69 | * 文件字节大小
70 | */
71 | private Long fileByteSize;
72 | /**
73 | * 文件格式化大小
74 | */
75 | private String fileFormatSize;
76 |
77 | /**
78 | * 上传人id
79 | */
80 | private Long userId;
81 |
82 | /**
83 | * 创建时间(上传时间)
84 | */
85 | @TableField(fill = FieldFill.INSERT)
86 | private LocalDateTime createTime;
87 |
88 | /**
89 | * 修改时间
90 | */
91 | @TableField(fill = FieldFill.INSERT_UPDATE)
92 | private LocalDateTime updateTime;
93 |
94 | }
--------------------------------------------------------------------------------
/minio-chunk-demo/src/main/java/com/fox/miniodemo/service/MediaFileService.java:
--------------------------------------------------------------------------------
1 | package com.fox.miniodemo.service;
2 |
3 | import com.baomidou.mybatisplus.extension.service.IService;
4 | import com.fox.miniodemo.entity.Result;
5 | import com.fox.miniodemo.po.MediaFile;
6 |
7 | /**
8 | *
9 | * 第三方服务-媒资文件表 服务类
10 | *
11 | *
12 | * @author 狐狸半面添
13 | * @since 2023-02-08
14 | */
15 | public interface MediaFileService extends IService {
16 |
17 | /**
18 | * 文件上传前检查文件是否存在
19 | *
20 | * @param fileMd5 需要上传的文件的md5值
21 | * @return 是否存在, false-不存在 true-存在
22 | */
23 | Result checkFile(String fileMd5);
24 |
25 | /**
26 | * 分块文件上传前检测分块文件是否已存在
27 | *
28 | * @param fileMd5 分块文件的源文件md5
29 | * @param chunkIndex 分块文件索引
30 | * @return 是否存在, false-不存在 true-存在
31 | */
32 | Result checkChunk(String fileMd5, Integer chunkIndex);
33 |
34 | /**
35 | * 上传分块文件
36 | *
37 | * @param fileMd5 原文件md5值
38 | * @param chunkIndex 分块文件索引
39 | * @param bytes 分块文件的字节数组形式
40 | * @return 上传情况
41 | */
42 | Result uploadChunk(String fileMd5, Integer chunkIndex, byte[] bytes);
43 |
44 | /**
45 | * 合并分块文件
46 | *
47 | * @param fileMd5 文件的md5十六进制值
48 | * @param fileName 文件名
49 | * @param tag 文件标签
50 | * @param chunkTotal 文件块总数
51 | * @return 合并与上传情况
52 | */
53 | Result uploadMergeChunks(String fileMd5, String fileName, String tag, Integer chunkTotal);
54 | }
55 |
--------------------------------------------------------------------------------
/minio-chunk-demo/src/main/java/com/fox/miniodemo/util/CipherUtils.java:
--------------------------------------------------------------------------------
1 | package com.fox.miniodemo.util;
2 |
3 | import javax.crypto.Cipher;
4 | import javax.crypto.KeyGenerator;
5 | import javax.crypto.SecretKey;
6 | import javax.crypto.spec.SecretKeySpec;
7 | import java.security.SecureRandom;
8 | import java.util.Base64;
9 |
10 | /**
11 | * AES加密解密工具
12 | *
13 | * @author 狐狸半面添
14 | * @create 2023-01-18 20:34
15 | */
16 | public class CipherUtils {
17 |
18 | private static final String SECRET_KEY = "tangyulang5201314";
19 | private static final String AES = "AES";
20 | private static final String CHARSET_NAME = "UTF-8";
21 |
22 | /**
23 | * 生成密钥 key
24 | *
25 | * @param password 加密密码
26 | * @return
27 | * @throws Exception
28 | */
29 | private static SecretKeySpec generateKey(String password) throws Exception {
30 | // 1.构造密钥生成器,指定为AES算法,不区分大小写
31 | KeyGenerator keyGenerator = KeyGenerator.getInstance(AES);
32 | // 2. 因为AES要求密钥的长度为128,我们需要固定的密码,因此随机源的种子需要设置为我们的密码数组
33 | // 生成一个128位的随机源, 根据传入的字节数组
34 | /*
35 | * 这种方式 windows 下正常, Linux 环境下会解密失败
36 | * keyGenerator.init(128, new SecureRandom(password.getBytes()));
37 | */
38 | // 兼容 Linux
39 | SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
40 | random.setSeed(password.getBytes());
41 | keyGenerator.init(128, random);
42 | // 3.产生原始对称密钥
43 | SecretKey original_key = keyGenerator.generateKey();
44 | // 4. 根据字节数组生成AES密钥
45 | return new SecretKeySpec(original_key.getEncoded(), AES);
46 | }
47 |
48 | /**
49 | * 加密
50 | *
51 | * @param content 加密的内容
52 | * @param password 加密密码
53 | * @return
54 | */
55 | private static String aESEncode(String content, String password) {
56 | try {
57 | // 根据指定算法AES自成密码器
58 | Cipher cipher = Cipher.getInstance(AES);
59 | // 基于加密模式和密钥初始化Cipher
60 | cipher.init(Cipher.ENCRYPT_MODE, generateKey(password));
61 | // 单部分加密结束, 重置Cipher, 获取加密内容的字节数组(这里要设置为UTF-8)防止解密为乱码
62 | byte[] bytes = cipher.doFinal(content.getBytes(CHARSET_NAME));
63 | // 将加密后的字节数组转为字符串返回
64 | return Base64.getUrlEncoder().encodeToString(bytes);
65 | } catch (Exception e) {
66 | // 如果有错就返回 null
67 | return null;
68 | }
69 | }
70 |
71 | /**
72 | * 解密
73 | *
74 | * @param content 解密内容
75 | * @param password 解密密码
76 | * @return
77 | */
78 | private static String AESDecode(String content, String password) {
79 | try {
80 | // 将加密并编码后的内容解码成字节数组
81 | byte[] bytes = Base64.getUrlDecoder().decode(content);
82 | // 这里指定了算法为AES
83 | Cipher cipher = Cipher.getInstance(AES);
84 | // 基于解密模式和密钥初始化Cipher
85 | cipher.init(Cipher.DECRYPT_MODE, generateKey(password));
86 | // 单部分加密结束,重置Cipher
87 | byte[] result = cipher.doFinal(bytes);
88 | // 将解密后的字节数组转成 UTF-8 编码的字符串返回
89 | return new String(result, CHARSET_NAME);
90 | } catch (Exception e) {
91 | // 如果有错就返回 null
92 | return null;
93 | }
94 |
95 |
96 | }
97 |
98 | /**
99 | * 加密
100 | *
101 | * @param content 加密内容
102 | * @return 加密结果
103 | */
104 | public static String encrypt(String content) {
105 | return aESEncode(content, SECRET_KEY);
106 | }
107 |
108 | /**
109 | * 解密
110 | *
111 | * @param content 解密内容
112 | * @return 解密结果
113 | */
114 | public static String decrypt(String content) {
115 | try {
116 | return AESDecode(content, SECRET_KEY);
117 | } catch (Exception e) {
118 | return null;
119 | }
120 | }
121 | }
--------------------------------------------------------------------------------
/minio-chunk-demo/src/main/java/com/fox/miniodemo/util/FileFormatUtils.java:
--------------------------------------------------------------------------------
1 | package com.fox.miniodemo.util;
2 |
3 | import java.text.DecimalFormat;
4 |
5 | /**
6 | * 文件格式工具
7 | *
8 | * @author 狐狸半面添
9 | * @create 2023-02-09 19:43
10 | */
11 | public class FileFormatUtils {
12 | /**
13 | * 将字节单位的文件大小转为格式化的文件大小表示
14 | *
15 | * @param fileLength 文件字节大小
16 | * @return 格式化文件大小表示
17 | */
18 | public static String formatFileSize(long fileLength) {
19 | DecimalFormat df = new DecimalFormat("#.00");
20 | String fileSizeString = "";
21 | String wrongSize = "0B";
22 | if (fileLength == 0) {
23 | return wrongSize;
24 | }
25 | if (fileLength < 1024) {
26 | fileSizeString = df.format((double) fileLength) + " B";
27 | } else if (fileLength < 1048576) {
28 | fileSizeString = df.format((double) fileLength / 1024) + " KB";
29 | } else if (fileLength < 1073741824) {
30 | fileSizeString = df.format((double) fileLength / 1048576) + " MB";
31 | } else {
32 | fileSizeString = df.format((double) fileLength / 1073741824) + " GB";
33 | }
34 | return fileSizeString;
35 | }
36 | }
--------------------------------------------------------------------------------
/minio-chunk-demo/src/main/java/com/fox/miniodemo/util/FileTypeUtils.java:
--------------------------------------------------------------------------------
1 | package com.fox.miniodemo.util;
2 |
3 | import org.apache.tika.metadata.HttpHeaders;
4 | import org.apache.tika.metadata.Metadata;
5 | import org.apache.tika.metadata.TikaCoreProperties;
6 | import org.apache.tika.mime.MediaType;
7 | import org.apache.tika.parser.AutoDetectParser;
8 | import org.apache.tika.parser.ParseContext;
9 | import org.apache.tika.parser.Parser;
10 | import org.xml.sax.helpers.DefaultHandler;
11 |
12 | import java.io.File;
13 | import java.io.InputStream;
14 | import java.nio.file.Files;
15 | import java.util.HashMap;
16 | import java.util.Map;
17 |
18 | /**
19 | * 文件类型工具类
20 | *
21 | * @author 狐狸半面添
22 | * @create 2023-02-09 0:13
23 | */
24 | public class FileTypeUtils {
25 | private static final Map contentType = new HashMap<>();
26 |
27 | /**
28 | * 获取文件的 mime 类型
29 | *
30 | * @param file 文件
31 | * @return mime类型
32 | */
33 | public static String getMimeType(File file) {
34 | AutoDetectParser parser = new AutoDetectParser();
35 | parser.setParsers(new HashMap());
36 | Metadata metadata = new Metadata();
37 | metadata.add(TikaCoreProperties.RESOURCE_NAME_KEY, file.getName());
38 | try (InputStream stream = Files.newInputStream(file.toPath())) {
39 | parser.parse(stream, new DefaultHandler(), metadata, new ParseContext());
40 | } catch (Exception e) {
41 | throw new RuntimeException();
42 | }
43 | return metadata.get(HttpHeaders.CONTENT_TYPE);
44 | }
45 |
46 | /**
47 | * 根据 mimetype 获取文件的简单类型
48 | *
49 | * @param mimeType mime类型
50 | * @return 简单类型:文本,图片,音频,视频,其它
51 | */
52 | public static String getSimpleType(String mimeType) {
53 | String simpleType = mimeType.split("/")[0];
54 | switch (simpleType) {
55 | case "text":
56 | return "文本";
57 | case "image":
58 | return "图片";
59 | case "audio":
60 | return "音频";
61 | case "video":
62 | return "视频";
63 | case "application":
64 | return "其它";
65 | default:
66 | throw new RuntimeException("mimeType格式错误");
67 | }
68 | }
69 |
70 | // 测试
71 | public static void main(String[] args) {
72 | File file = new File("D:\\location语法规则.docx");
73 | String mimeType = getMimeType(file);
74 | System.out.println(mimeType);
75 | System.out.println(getSimpleType(mimeType));
76 | }
77 | }
--------------------------------------------------------------------------------
/minio-chunk-demo/src/main/java/com/fox/miniodemo/util/MinioClientUtils.java:
--------------------------------------------------------------------------------
1 | package com.fox.miniodemo.util;
2 |
3 | import com.j256.simplemagic.ContentInfo;
4 | import com.j256.simplemagic.ContentInfoUtil;
5 | import io.minio.GetObjectArgs;
6 | import io.minio.MinioClient;
7 | import io.minio.PutObjectArgs;
8 | import io.minio.UploadObjectArgs;
9 | import org.apache.tomcat.util.http.fileupload.IOUtils;
10 | import org.springframework.http.MediaType;
11 |
12 | import java.io.*;
13 |
14 | /**
15 | * 操作minio的工具类
16 | *
17 | * @author 狐狸半面添
18 | * @create 2023-02-08 22:08
19 | */
20 | public class MinioClientUtils {
21 | private final MinioClient minioClient;
22 |
23 | public MinioClientUtils(MinioClient minioClient) {
24 | this.minioClient = minioClient;
25 | }
26 |
27 | /**
28 | * 获取minio文件的输入流对象
29 | *
30 | * @param bucket 桶
31 | * @param filePath 文件路径
32 | * @return 输入流
33 | * @throws Exception 异常
34 | */
35 | public InputStream getObject(String bucket, String filePath) throws Exception {
36 | return minioClient.getObject(GetObjectArgs.builder().bucket(bucket).object(filePath).build());
37 | }
38 |
39 | /**
40 | * 将分块文件上传到分布式文件系统
41 | *
42 | * @param bytes 文件的字节数组
43 | * @param bucket 桶
44 | * @param filePath 存储在桶中的文件路径
45 | */
46 | public void uploadChunkFile(byte[] bytes, String bucket, String filePath) throws Exception {
47 | // 1.指定资源的媒体类型为未知二进制流,以分片形式上传至minio
48 | try (
49 | ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes)
50 | ) {
51 | minioClient.putObject(
52 | PutObjectArgs.builder()
53 | .bucket(bucket)
54 | .object(filePath)
55 | // InputStream stream, long objectSize 对象大小, long partSize 分片大小(-1表示5M,最大不要超过5T,最多10000)
56 | .stream(byteArrayInputStream, byteArrayInputStream.available(), -1)
57 | .contentType(MediaType.APPLICATION_OCTET_STREAM_VALUE)
58 | .build()
59 | );
60 | } catch (Exception e) {
61 | throw new RuntimeException(e);
62 | }
63 | }
64 |
65 | /**
66 | * 将文件上传到分布式文件系统
67 | *
68 | * @param bytes 文件的字节数组
69 | * @param bucket 桶
70 | * @param filePath 存储在桶中的文件路径
71 | */
72 | public void uploadFile(byte[] bytes, String bucket, String filePath) throws Exception {
73 | // 1.指定资源的媒体类型,默认未知二进制流
74 | String contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
75 |
76 | // 2.判断是否有后缀,有后缀则根据后缀推算出文件类型,否则使用默认的未知二进制流
77 | if (filePath.contains(".")) {
78 | // 取objectName中的扩展名
79 | String extension = filePath.substring(filePath.lastIndexOf("."));
80 | ContentInfo extensionMatch = ContentInfoUtil.findExtensionMatch(extension);
81 | if (extensionMatch != null) {
82 | contentType = extensionMatch.getMimeType();
83 | }
84 | }
85 |
86 | // 3.以分片形式上传至minio
87 | ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
88 |
89 | PutObjectArgs putObjectArgs = PutObjectArgs.builder()
90 | .bucket(bucket)
91 | .object(filePath)
92 | // InputStream stream, long objectSize 对象大小, long partSize 分片大小(-1表示5M,最大不要超过5T,最多10000)
93 | .stream(byteArrayInputStream, byteArrayInputStream.available(), -1)
94 | .contentType(contentType)
95 | .build();
96 | // 上传
97 | minioClient.putObject(putObjectArgs);
98 | }
99 |
100 |
101 | /**
102 | * 根据文件路径将文件上传到文件系统
103 | *
104 | * @param naiveFilePath 本地文件路径
105 | * @param bucket 桶
106 | * @param minioFilePath 保存到minio的文件路径位置
107 | * @throws Exception 异常
108 | */
109 | public void uploadChunkFile(String naiveFilePath, String bucket, String minioFilePath) throws Exception {
110 | UploadObjectArgs uploadObjectArgs = UploadObjectArgs.builder()
111 | .bucket(bucket)
112 | .object(minioFilePath)
113 | .filename(naiveFilePath)
114 | .build();
115 | minioClient.uploadObject(uploadObjectArgs);
116 | }
117 |
118 | /**
119 | * 下载文件保存至本地临时文件中
120 | *
121 | * @param tempFilePrefix 临时文件的前缀
122 | * @param tempFileSuffix 临时文件的后缀
123 | * @param bucket 桶
124 | * @param filePath 文件路径
125 | * @return 携带数据的临时文件
126 | * @throws Exception 异常信息
127 | */
128 | public File downloadFile(String tempFilePrefix, String tempFileSuffix, String bucket, String filePath) throws Exception {
129 | // 1.创建空文件,临时保存下载下来的分块文件数据
130 | File tempFile = File.createTempFile(tempFilePrefix, tempFileSuffix);
131 | try (
132 | // 2.获取目标文件的输入流对象
133 | InputStream inputStream = getObject(bucket, filePath);
134 | // 3.获取临时空文件的输出流对象
135 | FileOutputStream outputStream = new FileOutputStream(tempFile);
136 | ) {
137 | // 4.进行数据拷贝
138 | IOUtils.copy(inputStream, outputStream);
139 | // 5.返回保存了数据的临时文件
140 | return tempFile;
141 | } catch (Exception e) {
142 | throw new RuntimeException(e.getMessage());
143 | }
144 | }
145 |
146 | // public File downloadFile(String tempFilePrefix, String tempFileSuffix, String bucket, String filePath) throws Exception {
147 | // // 1.创建空文件,临时保存下载下来的分块文件数据
148 | // File tempFile = File.createTempFile(tempFilePrefix, tempFileSuffix);
149 | // try {
150 | // Long start = System.currentTimeMillis();
151 | // minioClient.downloadObject(
152 | // DownloadObjectArgs.builder()
153 | // // 指定 bucket 存储桶
154 | // .bucket(bucket)
155 | // // 指定 哪个文件
156 | // .object(filePath)
157 | // // 指定存放位置与名称
158 | // .filename(tempFile.getPath())
159 | // .build());
160 | // Long end = System.currentTimeMillis();
161 | // System.out.println("下载分块时间:"+(end-start)+"ms");
162 | // // 5.返回保存了数据的临时文件
163 | // return tempFile;
164 | // } catch (Exception e) {
165 | // throw new RuntimeException(e.getMessage());
166 | // }
167 | // }
168 | }
169 |
--------------------------------------------------------------------------------
/minio-chunk-demo/src/main/java/com/fox/miniodemo/util/RegexUtils.java:
--------------------------------------------------------------------------------
1 | package com.fox.miniodemo.util;
2 |
3 | import cn.hutool.core.util.StrUtil;
4 |
5 | /**
6 | * 校验格式工具类
7 | *
8 | * @author 狐狸半面添
9 | * @create 2023-01-16 22:17
10 | */
11 | public class RegexUtils {
12 |
13 | /**
14 | * 正则表达式模板
15 | *
16 | * @author 狐狸半面添
17 | * @create 2023-01-16 22:39
18 | */
19 | public static class RegexPatterns {
20 |
21 | /**
22 | * md5十六进制正则:32个字符
23 | */
24 | public static final String MD5_HEX_REGEX = "^[0-9abcdef]{32}$";
25 |
26 | /**
27 | * 文件名称正则:最多255个字符
28 | */
29 | public static final String FILE_NAME_REGEX = "^.{1,255}$";
30 |
31 | /**
32 | * 文件标签正则:最多32个字符
33 | */
34 | public static final String FILE_TAG_REGEX = "^.{1,32}$";
35 |
36 | }
37 |
38 |
39 | /**
40 | * 校验是否不符合正则格式
41 | *
42 | * @param str 字符串
43 | * @param regex 正则表达式
44 | * @return true:符合 false:不符合
45 | */
46 | private static boolean mismatch(String str, String regex) {
47 | if (StrUtil.isBlank(str)) {
48 | return true;
49 | }
50 | return !str.matches(regex);
51 | }
52 |
53 |
54 | /**
55 | * 是否是无效十六进制md5格式
56 | *
57 | * @param md5Hex md5的十六进制
58 | * @return true:符合,false:不符合
59 | */
60 | public static boolean isMd5HexInvalid(String md5Hex){
61 | return mismatch(md5Hex, RegexPatterns.MD5_HEX_REGEX);
62 | }
63 |
64 | }
--------------------------------------------------------------------------------
/minio-chunk-demo/src/main/java/com/fox/miniodemo/vo/CheckChunkFileVO.java:
--------------------------------------------------------------------------------
1 | package com.fox.miniodemo.vo;
2 |
3 | import com.fox.miniodemo.util.RegexUtils;
4 | import lombok.AllArgsConstructor;
5 | import lombok.Data;
6 | import lombok.NoArgsConstructor;
7 |
8 | import javax.validation.constraints.Min;
9 | import javax.validation.constraints.NotBlank;
10 | import javax.validation.constraints.NotNull;
11 | import javax.validation.constraints.Pattern;
12 |
13 | /**
14 | * 分块文件上传检查封装类
15 | *
16 | * @author 狐狸半面添
17 | * @create 2023-02-08 16:54
18 | */
19 | @Data
20 | @NoArgsConstructor
21 | @AllArgsConstructor
22 | public class CheckChunkFileVO {
23 | /**
24 | * 需要上传的文件的md5值
25 | */
26 | @NotBlank(message = "文件md5不能为空")
27 | @Pattern(regexp = RegexUtils.RegexPatterns.MD5_HEX_REGEX, message = "文件md5格式错误")
28 | private String fileMd5;
29 | /**
30 | * 该分块文件的索引
31 | */
32 | @NotNull(message = "分块文件索引不能为空")
33 | @Min(value = 0, message = "分块文件索引必须是大于等于0的整数")
34 | private Integer chunkIndex;
35 | }
36 |
--------------------------------------------------------------------------------
/minio-chunk-demo/src/main/java/com/fox/miniodemo/vo/UploadMergeChunksVO.java:
--------------------------------------------------------------------------------
1 | package com.fox.miniodemo.vo;
2 |
3 | import com.fox.miniodemo.util.RegexUtils;
4 | import lombok.AllArgsConstructor;
5 | import lombok.Data;
6 | import lombok.NoArgsConstructor;
7 |
8 | import javax.validation.constraints.Min;
9 | import javax.validation.constraints.NotBlank;
10 | import javax.validation.constraints.NotNull;
11 | import javax.validation.constraints.Pattern;
12 |
13 | /**
14 | * @author 狐狸半面添
15 | * @create 2023-02-09 22:56
16 | */
17 | @Data
18 | @NoArgsConstructor
19 | @AllArgsConstructor
20 | public class UploadMergeChunksVO {
21 | @NotBlank(message = "文件md5不能为空")
22 | @Pattern(regexp = RegexUtils.RegexPatterns.MD5_HEX_REGEX, message = "文件md5格式错误")
23 | private String fileMd5;
24 |
25 | @NotBlank(message = "文件名不能为空")
26 | @Pattern(regexp = RegexUtils.RegexPatterns.FILE_NAME_REGEX, message = "文件名最多255个字符")
27 | private String fileName;
28 |
29 | @NotBlank(message = "文件标签不能为空")
30 | @Pattern(regexp = RegexUtils.RegexPatterns.FILE_TAG_REGEX, message = "文件标签最多32个字符")
31 | private String tag;
32 |
33 | @NotNull(message = "分块文件数不能为空")
34 | @Min(value = 1, message = "块总数必须大于等于1")
35 | private Integer chunkTotal;
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/minio-chunk-demo/src/main/resources/application.yaml:
--------------------------------------------------------------------------------
1 | server:
2 | port: 60000
3 | spring:
4 | main:
5 | allow-circular-references: true # 允许循环依赖
6 | servlet:
7 | multipart:
8 | max-file-size: 3MB
9 | max-request-size: 5MB
10 | datasource:
11 | username: root
12 | password: 123456
13 | url: jdbc:mysql://127.0.0.1:3306/minio_demo?characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8
14 | driver-class-name: com.mysql.cj.jdbc.Driver
15 | # 指定数据源
16 | type: com.alibaba.druid.pool.DruidDataSource
17 | # Spring Boot 默认是不注入这些属性值的,需要自己绑定
18 | # druid 数据源专有配置
19 | initialSize: 5
20 | minIdle: 5
21 | maxActive: 20
22 | maxWait: 60000
23 | timeBetweenEvictionRunsMillis: 60000
24 | minEvictableIdleTimeMillis: 300000
25 | validationQuery: SELECT 1 FROM DUAL
26 | testWhileIdle: true
27 | testOnBorrow: false
28 | testOnReturn: false
29 | poolPreparedStatements: true
30 | mybatis-plus:
31 | mapper-locations: classpath:/mapper/**/*.xml
32 | global-config:
33 | db-config:
34 | id-type: assign_id # 使用雪花算法生成id
35 | logic-delete-value: 1 # 逻辑已删除值
36 | logic-not-delete-value: 0 # 逻辑未删除值
37 | configuration:
38 | # 这里我们配置出底层的sql,可以输出sql日志信息,方便我们观察
39 | log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
40 | minio:
41 | # 指定连接的ip和端口
42 | endpoint: http://192.168.65.129:9000
43 | # 指定 访问秘钥(也称用户id)
44 | accessKey: minioadmin
45 | # 指定 私有秘钥(也称密码)
46 | secretKey: minioadmin
--------------------------------------------------------------------------------
/qrcode-demo/README.md:
--------------------------------------------------------------------------------
1 | 1.具体说明文档参考:[Java生成二维码(前后端分离项目实战)](https://blog.csdn.net/qq_62982856/article/details/132572246)
--------------------------------------------------------------------------------
/qrcode-demo/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | org.springframework.boot
9 | spring-boot-starter-parent
10 | 3.1.2
11 |
12 |
13 |
14 | com.zhulang
15 | qrcode-demo
16 | 1.0-SNAPSHOT
17 |
18 |
19 | 17
20 | 17
21 | UTF-8
22 |
23 |
24 |
25 |
26 | org.springframework.boot
27 | spring-boot-starter-web
28 |
29 |
30 |
31 |
32 | com.google.zxing
33 | javase
34 | 3.1.0
35 |
36 |
37 |
38 |
39 | commons-lang
40 | commons-lang
41 | 2.6
42 |
43 |
44 |
45 | com.github.liuyueyi.media
46 | qrcode-plugin
47 | 3.0.0
48 |
49 |
50 |
51 |
52 |
53 |
54 | org.springframework.boot
55 | spring-boot-maven-plugin
56 |
57 |
58 |
59 | org.projectlombok
60 | lombok
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/qrcode-demo/src/main/java/com/zhulang/qrcode/Application.java:
--------------------------------------------------------------------------------
1 | package com.zhulang.qrcode;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | /**
7 | * @author 狐狸半面添
8 | * @create 2023-08-28 21:26
9 | */
10 | @SpringBootApplication
11 | public class Application {
12 | public static void main(String[] args) {
13 | SpringApplication.run(Application.class,args);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/qrcode-demo/src/main/java/com/zhulang/qrcode/controller/QrCodePluginController.java:
--------------------------------------------------------------------------------
1 | package com.zhulang.qrcode.controller;
2 |
3 | import com.github.hui.quick.plugin.qrcode.wrapper.QrCodeGenWrapper;
4 | import com.github.hui.quick.plugin.qrcode.wrapper.QrCodeOptions;
5 | import com.google.zxing.WriterException;
6 | import com.zhulang.qrcode.util.QrCodePluginUtils;
7 | import com.zhulang.qrcode.util.ZXingUtils;
8 | import org.springframework.web.bind.annotation.*;
9 | import org.springframework.web.multipart.MultipartFile;
10 |
11 | import java.awt.*;
12 | import java.awt.image.BufferedImage;
13 | import java.io.IOException;
14 | import java.util.HashMap;
15 | import java.util.Map;
16 |
17 | /**
18 | * @author 狐狸半面添
19 | * @create 2023-08-29 21:37
20 | */
21 | @CrossOrigin
22 | @RestController
23 | @RequestMapping("/qrcode-plugin")
24 | public class QrCodePluginController {
25 |
26 | /**
27 | * 生成普通黑白二维码
28 | *
29 | * @param content 文本内容
30 | * @return 图片base64编码
31 | */
32 | @GetMapping("/getCommonBlackWhiteCode")
33 | public Map getCommonBlackWhiteCode(@RequestParam("content") String content) throws IOException, WriterException {
34 | Map map = new HashMap<>(1);
35 | map.put("imgEncode", QrCodePluginUtils.generateBlackWhiteCode(content));
36 | return map;
37 | }
38 |
39 | /**
40 | * 生成带 logo 的黑白二维码
41 | *
42 | * @param content 文本内容
43 | * @param logo logo文件
44 | * @return 图片base64编码
45 | */
46 | @PostMapping("/getLogoBlackWhiteCode")
47 | public Map getLogoBlackWhiteCode(@RequestParam("content") String content, @RequestParam("logo") MultipartFile logo) throws IOException, WriterException {
48 | Map map = new HashMap<>(1);
49 | map.put("imgEncode", QrCodePluginUtils.generateLogoBlackWhiteCode(content, logo));
50 | return map;
51 | }
52 |
53 | /**
54 | * 生成彩色二维码
55 | *
56 | * @param content 文本内容
57 | * @return 图片base64编码
58 | */
59 | @GetMapping("/getColorCode")
60 | public Map getColorCode(@RequestParam("content") String content) throws IOException, WriterException {
61 | Map map = new HashMap<>(1);
62 | // 二维码颜色可以由前端传过来进行指定
63 | map.put("imgEncode", QrCodePluginUtils.generateColorCode(content, Color.BLUE));
64 | return map;
65 | }
66 |
67 | /**
68 | * 生成带背景图片的黑白二维码
69 | *
70 | * @param content 文本内容
71 | * @param backgroundImage 背景图文件
72 | * @return 图片base64编码
73 | */
74 | @PostMapping("/getBgBlackWhiteCode")
75 | public Map getBgBlackWhiteCode(@RequestParam("content") String content, @RequestParam("backgroundImage") MultipartFile backgroundImage) throws IOException, WriterException {
76 | Map map = new HashMap<>(1);
77 | map.put("imgEncode", QrCodePluginUtils.generateBgBlackWhiteCode(content, backgroundImage));
78 | return map;
79 | }
80 |
81 | /**
82 | * 生成带特殊形状的二维码
83 | *
84 | * @param content 文本内容
85 | * @return 图片base64编码
86 | */
87 | @GetMapping("/getShapeCode")
88 | public Map getShapeCode(@RequestParam("content") String content) throws IOException, WriterException {
89 | Map map = new HashMap<>(1);
90 | // 绘制样式可以由前端传过来进行指定,这里设定为 钻石
91 | map.put("imgEncode", QrCodePluginUtils.generateShapeCode(content, QrCodeOptions.DrawStyle.DIAMOND));
92 | return map;
93 | }
94 |
95 | /**
96 | * 生成图片填充二维码
97 | *
98 | * @param content 文本内容
99 | * @param fillImg 填充图片
100 | * @return 图片base64编码
101 | */
102 | @PostMapping("/getImgFillCode")
103 | public Map getImgFillCode(@RequestParam("content") String content, @RequestParam("fillImg") MultipartFile fillImg) throws IOException, WriterException {
104 | Map map = new HashMap<>(1);
105 | map.put("imgEncode", QrCodePluginUtils.generateImgFillCode(content, fillImg));
106 | return map;
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/qrcode-demo/src/main/java/com/zhulang/qrcode/controller/ZXingController.java:
--------------------------------------------------------------------------------
1 | package com.zhulang.qrcode.controller;
2 |
3 | import com.google.zxing.WriterException;
4 | import com.zhulang.qrcode.util.ZXingUtils;
5 | import org.springframework.web.bind.annotation.*;
6 | import org.springframework.web.multipart.MultipartFile;
7 |
8 | import java.io.IOException;
9 | import java.util.HashMap;
10 | import java.util.Map;
11 |
12 | /**
13 | * @author 狐狸半面添
14 | * @create 2023-08-28 21:35
15 | */
16 | @CrossOrigin
17 | @RestController
18 | @RequestMapping("/zxing")
19 | public class ZXingController {
20 | @GetMapping("/getCommonBlackWhite")
21 | public Map getCommonBlackWhite(@RequestParam("content") String content) throws IOException, WriterException {
22 | Map map = new HashMap<>(1);
23 | map.put("imgEncode", ZXingUtils.generateBlackWhiteCode(content));
24 | return map;
25 | }
26 |
27 | @PostMapping("/getLogo")
28 | public Map getLogo(@RequestParam("content") String content, @RequestParam("logo") MultipartFile logo) throws IOException, WriterException {
29 | Map map = new HashMap<>(1);
30 | map.put("imgEncode", ZXingUtils.generateLogoCode(content, logo));
31 | return map;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/qrcode-demo/src/main/java/com/zhulang/qrcode/util/QrCodePluginUtils.java:
--------------------------------------------------------------------------------
1 | package com.zhulang.qrcode.util;
2 |
3 | import com.github.hui.quick.plugin.qrcode.wrapper.QrCodeGenWrapper;
4 | import com.github.hui.quick.plugin.qrcode.wrapper.QrCodeOptions;
5 | import com.google.zxing.WriterException;
6 | import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
7 | import org.apache.tomcat.util.codec.binary.Base64;
8 | import org.springframework.util.FastByteArrayOutputStream;
9 | import org.springframework.web.multipart.MultipartFile;
10 |
11 | import javax.imageio.ImageIO;
12 | import java.awt.*;
13 | import java.awt.image.BufferedImage;
14 | import java.io.IOException;
15 |
16 | /**
17 | * @author 狐狸半面添
18 | * @create 2023-08-29 21:41
19 | */
20 | public class QrCodePluginUtils {
21 |
22 | private static String imageParseBase64(BufferedImage image) throws IOException {
23 | FastByteArrayOutputStream fos = new FastByteArrayOutputStream();
24 | ImageIO.write(image, "png", fos);
25 | // 获取二维码图片的 base64 编码
26 | String imgEncode = Base64.encodeBase64String(fos.toByteArray());
27 | fos.flush();
28 | return imgEncode;
29 | }
30 |
31 | /**
32 | * 生成普通黑白二维码
33 | *
34 | * @param content 文本内容
35 | * @return 图片base64编码
36 | */
37 | public static String generateBlackWhiteCode(String content) throws IOException, WriterException {
38 | return imageParseBase64(QrCodeGenWrapper.of(content).asBufferedImage());
39 | }
40 |
41 | /**
42 | * 生成带 logo 的黑白二维码
43 | *
44 | * @param content 文本内容
45 | * @param logo logo文件
46 | * @return 图片base64编码
47 | */
48 | public static String generateLogoBlackWhiteCode(String content, MultipartFile logo) throws IOException, WriterException {
49 | BufferedImage image = QrCodeGenWrapper.of(content)
50 | .setLogo(logo.getInputStream())
51 | // 设置 logo 图片与二维码之间的比例,10 表示 logo 的宽度等于二维码的 1/10
52 | .setLogoRate(10)
53 | // 设置 logo 图片的样式,将 logo 的边框形状设置为圆形
54 | .setLogoStyle(QrCodeOptions.LogoStyle.ROUND)
55 | .asBufferedImage();
56 | return imageParseBase64(image);
57 | }
58 |
59 | /**
60 | * 生成彩色二维码
61 | *
62 | * @param content 文本内容
63 | * @param color 颜色
64 | * @return 图片base64编码
65 | */
66 | public static String generateColorCode(String content, Color color) throws IOException, WriterException {
67 | BufferedImage image = QrCodeGenWrapper.of(content)
68 | // 指定画笔颜色
69 | .setDrawPreColor(color)
70 | .asBufferedImage();
71 | return imageParseBase64(image);
72 | }
73 |
74 | /**
75 | * 生成带背景图片的黑白二维码
76 | *
77 | * @param content 文本内容
78 | * @param backgroundImage 背景图文件
79 | * @return 图片base64编码
80 | */
81 | public static String generateBgBlackWhiteCode(String content, MultipartFile backgroundImage) throws IOException, WriterException {
82 | BufferedImage image = QrCodeGenWrapper.of(content)
83 | // 设置背景图
84 | .setBgImg(backgroundImage.getInputStream())
85 | // 设置背景图透明度
86 | .setBgOpacity(0.7F)
87 | .asBufferedImage();
88 | return imageParseBase64(image);
89 | }
90 |
91 | /**
92 | * 生成带特殊形状的二维码
93 | *
94 | * @param content 文本内容
95 | * @param drawStyle 绘制样式
96 | * @return 图片base64编码
97 | */
98 | public static String generateShapeCode(String content, QrCodeOptions.DrawStyle drawStyle) throws IOException, WriterException {
99 | BufferedImage image = QrCodeGenWrapper.of(content)
100 | // 启用二维码绘制时的缩放功能
101 | .setDrawEnableScale(true)
102 | // 指定绘制样式
103 | .setDrawStyle(drawStyle)
104 | .asBufferedImage();
105 | return imageParseBase64(image);
106 | }
107 |
108 | /**
109 | * 生成图片填充二维码
110 | *
111 | * @param content 文本内容
112 | * @param fillImg 填充图片
113 | * @return 图片base64编码
114 | */
115 | public static String generateImgFillCode(String content, MultipartFile fillImg) throws IOException, WriterException {
116 | BufferedImage image = QrCodeGenWrapper.of(content)
117 | // 设置二维码的错误纠正级别
118 | .setErrorCorrection(ErrorCorrectionLevel.H)
119 | // 绘制二维码时采用图片填充
120 | .setDrawStyle(QrCodeOptions.DrawStyle.IMAGE)
121 | // 设置填充的图片
122 | .addImg(1, 1, fillImg.getInputStream())
123 | .asBufferedImage();
124 | return imageParseBase64(image);
125 | }
126 |
127 | }
128 |
--------------------------------------------------------------------------------
/qrcode-demo/src/main/java/com/zhulang/qrcode/util/ZXingUtils.java:
--------------------------------------------------------------------------------
1 | package com.zhulang.qrcode.util;
2 |
3 | import com.google.zxing.BarcodeFormat;
4 | import com.google.zxing.EncodeHintType;
5 | import com.google.zxing.MultiFormatWriter;
6 | import com.google.zxing.WriterException;
7 | import com.google.zxing.common.BitMatrix;
8 | import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
9 | import org.apache.tomcat.util.codec.binary.Base64;
10 | import org.springframework.util.FastByteArrayOutputStream;
11 | import org.springframework.web.multipart.MultipartFile;
12 |
13 | import javax.imageio.ImageIO;
14 | import java.awt.*;
15 | import java.awt.geom.RoundRectangle2D;
16 | import java.awt.image.BufferedImage;
17 | import java.io.IOException;
18 | import java.util.HashMap;
19 | import java.util.Map;
20 |
21 | /**
22 | * @author 狐狸半面添
23 | * @create 2023-08-28 21:35
24 | */
25 | public class ZXingUtils {
26 | public static String generateBlackWhiteCode(String content) throws WriterException, IOException {
27 | // 使用 Google 提供的 zxing 开源库,生成带 Logo 的黑白二维码
28 |
29 | // 需要创建一个 Map 集合,使用这个 Map 集合存储二维码相关的属性(参数)
30 | Map map = new HashMap<>(3);
31 |
32 | // 设置二维码的误差校正级别
33 | map.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
34 | // 设置二维码的字符集
35 | map.put(EncodeHintType.CHARACTER_SET, "utf-8");
36 | // 设置二维码四周的留白,单位为 px
37 | map.put(EncodeHintType.MARGIN, 1);
38 |
39 | // 创建 zxing 的核心对象,MultiFormatWriter(多格式写入器)
40 | // 通过 MultiFormatWriter 对象来生成二维码
41 | MultiFormatWriter writer = new MultiFormatWriter();
42 |
43 | // writer.encode(内容, 什么格式的二维码, 二维码宽度, 二维码高度, 二维码参数)
44 | // 位矩阵对象(位矩阵对象内部实际上是一个二维数组,二维数组中每一个元素是 boolean 类型,true 代表黑色,false 代表白色)
45 | BitMatrix bitMatrix = writer.encode(content, BarcodeFormat.QR_CODE, 300, 300, map);
46 |
47 | // 获取矩阵的宽度
48 | int width = bitMatrix.getWidth();
49 | // 获取矩阵的高度
50 | int height = bitMatrix.getHeight();
51 |
52 | // 生成二维码图片
53 | BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
54 |
55 | // 编写一个嵌套循环,遍历二维数组的一个循环,遍历位矩阵对象
56 | for (int x = 0; x < width; x++) {
57 | for (int y = 0; y < height; y++) {
58 | image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
59 | }
60 | }
61 |
62 | FastByteArrayOutputStream fos = new FastByteArrayOutputStream();
63 | ImageIO.write(image, "png", fos);
64 | // 获取二维码图片的 base64 编码
65 | String imgEncode = Base64.encodeBase64String(fos.toByteArray());
66 | fos.flush();
67 |
68 | // 返回 base64 编码
69 | return imgEncode;
70 | }
71 |
72 | public static String generateLogoCode(String content, MultipartFile logo) throws WriterException, IOException {
73 | // 使用 Google 提供的 zxing 开源库,生成普通的黑白二维码
74 |
75 | // 需要创建一个 Map 集合,使用这个 Map 集合存储二维码相关的属性(参数)
76 | Map map = new HashMap<>(3);
77 |
78 | // 设置二维码的误差校正级别
79 | map.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
80 | // 设置二维码的字符集
81 | map.put(EncodeHintType.CHARACTER_SET, "utf-8");
82 | // 设置二维码四周的留白,单位为 px
83 | map.put(EncodeHintType.MARGIN, 1);
84 |
85 | // 创建 zxing 的核心对象,MultiFormatWriter(多格式写入器)
86 | // 通过 MultiFormatWriter 对象来生成二维码
87 | MultiFormatWriter writer = new MultiFormatWriter();
88 |
89 | // writer.encode(内容, 什么格式的二维码, 二维码宽度, 二维码高度, 二维码参数)
90 | // 位矩阵对象(位矩阵对象内部实际上是一个二维数组,二维数组中每一个元素是 boolean 类型,true 代表黑色,false 代表白色)
91 | BitMatrix bitMatrix = writer.encode(content, BarcodeFormat.QR_CODE, 300, 300, map);
92 |
93 | // 获取矩阵的宽度
94 | int width = bitMatrix.getWidth();
95 | // 获取矩阵的高度
96 | int height = bitMatrix.getHeight();
97 |
98 | // 生成二维码图片
99 | BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
100 |
101 | // 编写一个嵌套循环,遍历二维数组的一个循环,遍历位矩阵对象
102 | for (int x = 0; x < width; x++) {
103 | for (int y = 0; y < height; y++) {
104 | image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
105 | }
106 | }
107 |
108 | // 给二维码添加 Logo
109 | // 1.获取 logo 图片:通过 ImageIO 的read方法,从输入流中读取,从而获得 logo 图片
110 | BufferedImage logoImage = ImageIO.read(logo.getInputStream());
111 | // 2.设置 logo 的宽度和高度
112 | int logoWidth = Math.min(logoImage.getWidth(), 60);
113 | int logoHeight = Math.min(logoImage.getHeight(), 60);
114 | // 3.将 logo 缩放:使用平滑缩放算法对原 logo 图像进行缩放得到一个全新的图像
115 | Image scaledLogo = logoImage.getScaledInstance(logoWidth, logoHeight, Image.SCALE_SMOOTH);
116 | // 4.将缩放后的 logo 画到黑白二维码上
117 | // 4.1 获取一个 2D 的画笔
118 | Graphics2D graphics2D = image.createGraphics();
119 | // 4.2 指定开始的坐标 x,y
120 | int x = (300 - logoWidth) / 2;
121 | int y = (300 - logoHeight) / 2;
122 | // 4.3 将缩放后的 logo 画上去
123 | graphics2D.drawImage(scaledLogo,x,y,null);
124 | // 4.4 创建一个具有指定位置、宽度、高度和圆角半径的圆角矩形,这个圆角矩形是用来绘制边框的
125 | Shape shape = new RoundRectangle2D.Float(x,y,logoWidth,logoHeight,10,10);
126 | // 4.5 使用一个宽度为 4px 的基本笔触
127 | graphics2D.setStroke(new BasicStroke(4f));
128 | // 4.6 给 logo 画圆角矩形
129 | graphics2D.draw(shape);
130 | // 4.7 释放画笔
131 | graphics2D.dispose();
132 |
133 | FastByteArrayOutputStream fos = new FastByteArrayOutputStream();
134 | ImageIO.write(image, "png", fos);
135 | // 获取二维码图片的 base64 编码
136 | String imgEncode = Base64.encodeBase64String(fos.toByteArray());
137 | fos.flush();
138 |
139 | // 返回 base64 编码
140 | return imgEncode;
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/qrcode-demo/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | servlet:
3 | multipart:
4 | max-file-size: 10MB
5 | file-size-threshold: 2MB
6 | max-request-size: 15MB
--------------------------------------------------------------------------------
/qrcode-demo/src/main/resources/static/html/qrcodePlugin/BgBlackWhiteCode.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 生成带背景图的黑白二维码
6 |
7 |
8 | 请输入文本内容:
9 | 请选择图片:
10 |
11 |
12 |
13 |
14 |
15 |
40 |
41 |
--------------------------------------------------------------------------------
/qrcode-demo/src/main/resources/static/html/qrcodePlugin/ColorCode.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 生成彩色二维码
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
30 |
31 |
--------------------------------------------------------------------------------
/qrcode-demo/src/main/resources/static/html/qrcodePlugin/CommonBlackWhiteCode.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 生成普通黑白二维码
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
30 |
31 |
--------------------------------------------------------------------------------
/qrcode-demo/src/main/resources/static/html/qrcodePlugin/ImgFillCode.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 生成图片填充二维码
6 |
7 |
8 | 请输入文本内容:
9 | 请选择图片:
10 |
11 |
12 |
13 |
14 |
15 |
40 |
41 |
--------------------------------------------------------------------------------
/qrcode-demo/src/main/resources/static/html/qrcodePlugin/LogoBlackWhiteCode.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 生成带 logo 的二维码
6 |
7 |
8 | 请输入文本内容:
9 | 请选择图片:
10 |
11 |
12 |
13 |
14 |
15 |
40 |
41 |
--------------------------------------------------------------------------------
/qrcode-demo/src/main/resources/static/html/qrcodePlugin/ShapeCode.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 生成特殊形状的二维码
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
30 |
31 |
--------------------------------------------------------------------------------
/qrcode-demo/src/main/resources/static/html/zxing/CommonBlackWhite.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 生成普通黑白二维码
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
30 |
31 |
--------------------------------------------------------------------------------
/qrcode-demo/src/main/resources/static/html/zxing/ZXingLogo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ZXing 生成带 logo 的二维码
6 |
7 |
8 | 请输入文本内容:
9 | 请选择图片:
10 |
11 |
12 |
13 |
14 |
15 |
40 |
41 |
--------------------------------------------------------------------------------
/sms-demo/README.md:
--------------------------------------------------------------------------------
1 | ## 1.application.yml 文件中的配置需要进行修改。
2 |
3 | - redis 地址
4 | - redis 密码
5 | - 短信平台信息
6 |
7 | ## 2.ubuntu安装docker的步骤
8 |
9 | 1. 安装需要的包
10 |
11 | ```shell
12 | sudo apt-get update
13 | ```
14 |
15 | 2. 安装依赖包
16 |
17 | ```shell
18 | sudo apt-get install \
19 | apt-transport-https \
20 | ca-certificates \
21 | curl \
22 | gnupg-agent \
23 | software-properties-common
24 | ```
25 |
26 | 3. 添加 Docker 的官方 GPG 密钥
27 |
28 | ```shell
29 | curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
30 | ```
31 |
32 | 4. 设置远程仓库
33 |
34 | ```shell
35 | sudo add-apt-repository \
36 | "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
37 | $(lsb_release -cs) \
38 | stable"
39 | ```
40 |
41 | 5. 安装 Docker-CE
42 |
43 | ```shell
44 | sudo apt-get update
45 |
46 | sudo apt-get install docker-ce docker-ce-cli containerd.io
47 | ```
48 |
49 | 6. 验证是否成功
50 |
51 | ```shell
52 | sudo docker run hello-world
53 | ```
54 |
55 | ## 3.使用 docker 安装redis并设置密码的步骤
56 |
57 | ```shell
58 | # 拉取redis镜像
59 | docker pull redis
60 |
61 | # 启动容器的时候,并为其设置密码
62 | docker run -d --name myredis -p 6379:6379 redis --requirepass "123456"
63 | ```
64 |
65 | ## 其它注意事项
66 |
67 | 注意需要将防火墙6379端口打开。
--------------------------------------------------------------------------------
/sms-demo/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.fox
8 | sms-demo
9 | 1.0-SNAPSHOT
10 |
11 |
12 | 8
13 | 8
14 | UTF-8
15 |
16 |
17 |
18 | org.springframework.boot
19 | spring-boot-starter-web
20 | 2.6.3
21 |
22 |
23 | org.projectlombok
24 | lombok
25 | 1.18.24
26 |
27 |
28 |
29 | com.alibaba
30 | fastjson
31 | 2.0.25
32 |
33 |
34 | cn.hutool
35 | hutool-all
36 | 5.8.15
37 |
38 |
39 |
40 | com.aliyun
41 | alibabacloud-dysmsapi20170525
42 | 2.0.23
43 |
44 |
45 |
46 | org.springframework.boot
47 | spring-boot-starter-data-redis
48 | 2.6.3
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/sms-demo/src/main/java/com/fox/sms/DirectTest/AliSmsTest.java:
--------------------------------------------------------------------------------
1 | package com.fox.sms.DirectTest;
2 |
3 | import com.aliyun.auth.credentials.Credential;
4 | import com.aliyun.auth.credentials.provider.StaticCredentialProvider;
5 | import com.aliyun.sdk.service.dysmsapi20170525.AsyncClient;
6 | import com.aliyun.sdk.service.dysmsapi20170525.models.SendSmsRequest;
7 | import com.aliyun.sdk.service.dysmsapi20170525.models.SendSmsResponse;
8 | import com.google.gson.Gson;
9 | import darabonba.core.client.ClientOverrideConfiguration;
10 |
11 | import java.util.concurrent.CompletableFuture;
12 |
13 | /**
14 | * 用于直接运行测试
15 | *
16 | * @author 狐狸半面添
17 | * @create 2023-04-01 18:47
18 | */
19 | public class AliSmsTest {
20 | public static void main(String[] args) throws Exception {
21 | StaticCredentialProvider provider = StaticCredentialProvider.create(Credential.builder()
22 | .accessKeyId("LTAI5tFhzvktt9U5j4ak2637")
23 | .accessKeySecret("hYLiytNltwB1pOetnBiNcXPmXqfvkx")
24 | //.securityToken("") // use STS token
25 | .build());
26 |
27 | // Configure the Client
28 | AsyncClient client = AsyncClient.builder()
29 | .region("cn-wulanchabu") // Region ID
30 | //.httpClient(httpClient) // Use the configured HttpClient, otherwise use the default HttpClient (Apache HttpClient)
31 | .credentialsProvider(provider)
32 | //.serviceConfiguration(Configuration.create()) // Service-level configuration
33 | // Client-level configuration rewrite, can set Endpoint, Http request parameters, etc.
34 | .overrideConfiguration(
35 | ClientOverrideConfiguration.create()
36 | .setEndpointOverride("dysmsapi.aliyuncs.com")
37 | //.setConnectTimeout(Duration.ofSeconds(30))
38 | )
39 | .build();
40 |
41 | // Parameter settings for API request
42 | SendSmsRequest sendSmsRequest = SendSmsRequest.builder()
43 | .signName("逐浪教育")
44 | .templateCode("SMS_275395309")
45 | .phoneNumbers("15675229376")
46 | .templateParam("{\"code\":\"123456\"}")
47 | // Request-level configuration rewrite, can set Http request parameters, etc.
48 | // .requestConfiguration(RequestConfiguration.create().setHttpHeaders(new HttpHeaders()))
49 | .build();
50 |
51 | // Asynchronously get the return value of the API request
52 | CompletableFuture response = client.sendSms(sendSmsRequest);
53 | // Synchronously get the return value of the API request
54 | SendSmsResponse resp = response.get();
55 | System.out.println(new Gson().toJson(resp));
56 | // Asynchronous processing of return values
57 | /*response.thenAccept(resp -> {
58 | System.out.println(new Gson().toJson(resp));
59 | }).exceptionally(throwable -> { // Handling exceptions
60 | System.out.println(throwable.getMessage());
61 | return null;
62 | });*/
63 |
64 | // Finally, close the client
65 | client.close();
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/sms-demo/src/main/java/com/fox/sms/SmsDemoApplication.java:
--------------------------------------------------------------------------------
1 | package com.fox.sms;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | /**
7 | * @author 狐狸半面添
8 | * @create 2023-03-22 18:07
9 | */
10 | @SpringBootApplication
11 | public class SmsDemoApplication {
12 | public static void main(String[] args) {
13 | SpringApplication.run(SmsDemoApplication.class, args);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/sms-demo/src/main/java/com/fox/sms/component/FastJsonRedisSerializer.java:
--------------------------------------------------------------------------------
1 | package com.fox.sms.component;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import com.alibaba.fastjson.parser.ParserConfig;
5 | import com.alibaba.fastjson.serializer.SerializerFeature;
6 | import com.fasterxml.jackson.databind.JavaType;
7 | import com.fasterxml.jackson.databind.type.TypeFactory;
8 | import org.springframework.data.redis.serializer.RedisSerializer;
9 | import org.springframework.data.redis.serializer.SerializationException;
10 |
11 | import java.nio.charset.Charset;
12 | import java.nio.charset.StandardCharsets;
13 |
14 | /**
15 | * Redis使用FastJson序列化
16 | *
17 | * @author 狐狸半面添
18 | * @create 2023-01-17 21:30
19 | */
20 | public class FastJsonRedisSerializer implements RedisSerializer {
21 |
22 | public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
23 |
24 | private Class clazz;
25 |
26 | static {
27 | ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
28 | }
29 |
30 | public FastJsonRedisSerializer(Class clazz) {
31 | super();
32 | this.clazz = clazz;
33 | }
34 |
35 | @Override
36 | public byte[] serialize(T t) throws SerializationException {
37 | if (t == null) {
38 | return new byte[0];
39 | }
40 | return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
41 | }
42 |
43 | @Override
44 | public T deserialize(byte[] bytes) throws SerializationException {
45 | if (bytes == null || bytes.length <= 0) {
46 | return null;
47 | }
48 | String str = new String(bytes, DEFAULT_CHARSET);
49 |
50 | return JSON.parseObject(str, clazz);
51 | }
52 |
53 |
54 | protected JavaType getJavaType(Class> clazz) {
55 | return TypeFactory.defaultInstance().constructType(clazz);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/sms-demo/src/main/java/com/fox/sms/config/RedisConfig.java:
--------------------------------------------------------------------------------
1 | package com.fox.sms.config;
2 |
3 | import com.fox.sms.component.FastJsonRedisSerializer;
4 | import com.fox.sms.util.RedisCacheUtils;
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.context.annotation.Configuration;
7 | import org.springframework.data.redis.connection.RedisConnectionFactory;
8 | import org.springframework.data.redis.core.RedisTemplate;
9 | import org.springframework.data.redis.serializer.StringRedisSerializer;
10 |
11 | /**
12 | * @author 狐狸半面添
13 | * @create 2023-01-17 21:00
14 | */
15 | @Configuration
16 | public class RedisConfig {
17 |
18 | @Bean
19 | @SuppressWarnings(value = { "unchecked", "rawtypes" })
20 | public RedisTemplate