数据库效果图:
代码部分:自己搭建minio测试 Result和配置文件可以自己编写 不需要和我一样
my.storage.minio.protocol = http
my.storage.minio.endPoint = 192.168.3.115:9000
my.storage.minio.stsEndPoint = 192.168.3.115:9000
my.storage.minio.accessKey = ZCEqAP5ECpdyt4pg9VIx
my.storage.minio.secretKey = dAlXmXdHWjYOZb8IWBowJGnGZzeKL9OC3TmYR8uO
my.storage.minio.internetProtocol = http
my.storage.minio.internetEndPoint = 192.168.3.115:9000
my.storage.minio.intranetProtocol = http
my.storage.minio.intranetEndPoint = 192.168.3.115:9000
public Result checkFileByMd5(FileCheckAndMergeForm fileCheckAndMergeForm) {
String md5 = fileCheckAndMergeForm.getMd5();
String bucket = fileCheckAndMergeForm.getBucket();
FileUploadInfo fileUploadInfo = (FileUploadInfo)redisUtil.get(md5);
if (fileUploadInfo != null) {
List<Integer> listParts = minioUtil.getListParts(fileUploadInfo.getObject(), fileUploadInfo.getUploadId(),bucket);
fileUploadInfo.setListParts(listParts);
return Result.ok("上传中")
.build("fileUploadInfo", fileUploadInfo);
}
ShardFilesUpload shardFilesUpload = shardFilesUploadMapper.selectShardFilesUploadByMd5(md5,bucket);
if (shardFilesUpload != null) {
FileUploadInfo dbFileInfo = BeanUtil.copyFrom(shardFilesUpload, FileUploadInfo.class);
return Result.ok("上传成功")
.build("fileUploadInfo", dbFileInfo);
}
return Result.ok("未上传");
}
@SneakyThrows
public Result initMultipartUpload(FileUploadInfo fileUploadInfo) {
FileUploadInfo redisFileUploadInfo = (FileUploadInfo)redisUtil.get(fileUploadInfo.getMd5());
String bucket = fileUploadInfo.getBucket();
// 若 redis 中有该 md5 的记录,以 redis 中为主
String object;
if (redisFileUploadInfo != null) {
fileUploadInfo = redisFileUploadInfo;
object = redisFileUploadInfo.getObject();
} else {
String originFileName = fileUploadInfo.getOriginFileName();
String suffix = FileUtil.extName(originFileName);
String fileName = FileUtil.mainName(originFileName);
// 对文件重命名,并以年月日文件夹格式存储
String nestFile = DateUtil.format(LocalDateTime.now(), "yyyy/MM/dd");
object = nestFile + "/" + fileName + "_" + fileUploadInfo.getMd5() + "." + suffix;
fileUploadInfo.setObject(object).setType(suffix);
}
UploadUrlsVO urlsVO;
// 单文件上传
if (fileUploadInfo.getChunkCount() == 1) {
urlsVO = minioUtil.getUploadObjectUrl(fileUploadInfo.getContentType(), object,bucket);
} else {
// 分片上传
urlsVO = minioUtil.initMultiPartUpload(fileUploadInfo, object,bucket);
}
fileUploadInfo.setUploadId(urlsVO.getUploadId());
// 存入 redis
redisUtil.set(fileUploadInfo.getMd5(), fileUploadInfo, 1, TimeUnit.DAYS);
return Result.ok()
.build("urlsVO", urlsVO);
}
public Result mergeMultipartUpload(FileCheckAndMergeForm fileCheckAndMergeForm){
String md5 = fileCheckAndMergeForm.getMd5();
String bucket = fileCheckAndMergeForm.getBucket();
FileUploadInfo redisFileUploadInfo = (FileUploadInfo)redisUtil.get(md5);
if (redisFileUploadInfo==null){
return Result.error("文件处理失败");
}
MyProperties.Minio minio = myProperties.getStorage().getMinio();
String url = String.format("%s://%s/%s/%s", minio.getProtocol(), minio.getEndPoint(), bucket, redisFileUploadInfo.getObject());
//获取真实的请求IP 如果是互联网地址,则将minio 地址替换
if (getRequest() != null) {
String ipAddr = IpUtil.getIpAddr(getRequest());
if (IpUtil.isExternalIP(ipAddr)) {
url = url.replaceFirst(minio.getProtocol(), minio.getInternetProtocol());
url = url.replaceFirst(minio.getEndPoint(), minio.getInternetEndPoint());
} else {
url = url.replaceFirst(minio.getProtocol(), minio.getIntranetProtocol());
url = url.replaceFirst(minio.getEndPoint(), minio.getIntranetEndPoint());
}
}
// 更新数据库
ShardFilesUpload shardFilesUpload = BeanUtil.copyFrom(redisFileUploadInfo, ShardFilesUpload.class);
shardFilesUpload.setId(UUID.randomUUID().toString());
shardFilesUpload.setUrl(url);
shardFilesUpload.setIsDelete(0);
shardFilesUpload.setBucket(bucket);
shardFilesUpload.setCreateTime(System.currentTimeMillis());
Integer chunkCount = redisFileUploadInfo.getChunkCount();
// 分片为 1 ,不需要合并,否则合并后看返回的是 true 还是 false
boolean isSuccess = chunkCount == 1 || minioUtil.mergeMultipartUpload(redisFileUploadInfo.getObject(), redisFileUploadInfo.getUploadId(),bucket);
if (isSuccess) {
shardFilesUploadMapper.insert(shardFilesUpload);
redisUtil.del(md5);
return Result.ok()
.build("storage", "minio")
.build("url", url);
}
return Result.error("文件处理失败");
}
/**
* 检查文件是否存在
*/
@RequestMapping (value = "/multipart/check", method = RequestMethod.POST)
public Result checkFileByMd5(@RequestBody @Valid FileCheckAndMergeForm fileCheckAndMergeForm) {
return sdkService.checkFileByMd5(fileCheckAndMergeForm);
}
/**
* 初始化文件分片地址及相关数据
*/
@RequestMapping (value = "/multipart/init", method = RequestMethod.POST)
public Result initMultiPartUpload(@RequestBody @Valid FileUploadInfo fileUploadInfo) {
return sdkService.initMultipartUpload(fileUploadInfo);
}
/**
* 文件合并(单文件不会合并,仅信息入库)
*/
@RequestMapping (value = "/multipart/merge", method = RequestMethod.POST)
public Result mergeMultipartUpload(@RequestBody @Valid FileCheckAndMergeForm fileCheckAndMergeForm) {
return sdkService.mergeMultipartUpload(fileCheckAndMergeForm);
}
Result checkFileByMd5(FileCheckAndMergeForm fileCheckAndMergeForm) ;
Result initMultipartUpload(FileUploadInfo fileUploadInfo) ;
Result mergeMultipartUpload(FileCheckAndMergeForm fileCheckAndMergeForm);
数据库是postgresSQL
DROP TABLE IF EXISTS religious.shard_files_upload;
CREATE TABLE religious.shard_files_upload
(
id VARCHAR(255) NOT NULL,
upload_id VARCHAR(255),
md5 VARCHAR(255),
url VARCHAR(255),
bucket VARCHAR(64),
object VARCHAR(255),
origin_file_name VARCHAR(255),
size BIGINT,
type VARCHAR(64),
chunk_size BIGINT,
chunk_count INTEGER,
is_delete INTEGER,
create_time BIGINT,
PRIMARY KEY (id)
);
COMMENT ON TABLE religious.shard_files_upload IS '分片上传记录明细表';
COMMENT ON COLUMN religious.shard_files_upload.upload_id IS '文件上传id';
COMMENT ON COLUMN religious.shard_files_upload.md5 IS '文件计算md5';
COMMENT ON COLUMN religious.shard_files_upload.url IS '文件访问地址';
COMMENT ON COLUMN religious.shard_files_upload.bucket IS '存储桶';
COMMENT ON COLUMN religious.shard_files_upload.object IS 'minio中文件名';
COMMENT ON COLUMN religious.shard_files_upload.origin_file_name IS '原始文件名';
COMMENT ON COLUMN religious.shard_files_upload.size IS '文件大小';
COMMENT ON COLUMN religious.shard_files_upload.type IS '文件类型';
COMMENT ON COLUMN religious.shard_files_upload.chunk_size IS '分片大小';
COMMENT ON COLUMN religious.shard_files_upload.chunk_count IS '分片数量';
COMMENT ON COLUMN religious.shard_files_upload.is_delete IS '是否删除';
COMMENT ON COLUMN religious.shard_files_upload.create_time IS '创建时间';
package net.junnan.religious.base.api.config;
import com.google.common.collect.Multimap;
import io.minio.CreateMultipartUploadResponse;
import io.minio.ListPartsResponse;
import io.minio.MinioAsyncClient;
import io.minio.ObjectWriteResponse;
import io.minio.messages.Part;
public class CustomMinioClient extends MinioAsyncClient {
/**
* 继承父类
* @param client
*/
public CustomMinioClient(MinioAsyncClient client) {
super(client);
}
/**
* 初始化分片上传、获取 uploadId
* @param bucket String 存储桶名称
* @param region String
* @param object String 文件名称
* @param headers Multimap<String, String> 请求头
* @param extraQueryParams Multimap<String, String>
* @return String
*/
public String initMultiPartUpload(String bucket, String region, String object, Multimap<String, String> headers, Multimap<String, String> extraQueryParams) throws Exception {
CreateMultipartUploadResponse response = super.createMultipartUploadAsync(bucket, region, object, headers, extraQueryParams).get();
return response.result().uploadId();
}
/**
* 合并分片
* @param bucketName String 桶名称
* @param region String
* @param objectName String 文件名称
* @param uploadId String 上传的 uploadId
* @param parts Part[] 分片集合
* @param extraHeaders Multimap<String, String>
* @param extraQueryParams Multimap<String, String>
* @return ObjectWriteResponse
*/
public ObjectWriteResponse mergeMultipartUpload(String bucketName, String region, String objectName, String uploadId, Part[] parts, Multimap<String, String> extraHeaders, Multimap<String, String> extraQueryParams) throws Exception {
return super.completeMultipartUploadAsync(bucketName, region, objectName, uploadId, parts, extraHeaders, extraQueryParams).get();
}
/**
* 查询当前上传后的分片信息
* @param bucketName String 桶名称
* @param region String
* @param objectName String 文件名称
* @param maxParts Integer 分片数量
* @param partNumberMarker Integer 分片起始值
* @param uploadId String 上传的 uploadId
* @param extraHeaders Multimap<String, String>
* @param extraQueryParams Multimap<String, String>
* @return ListPartsResponse
*/
public ListPartsResponse listMultipart(String bucketName, String region, String objectName, Integer maxParts, Integer partNumberMarker, String uploadId, Multimap<String, String> extraHeaders, Multimap<String, String> extraQueryParams) throws Exception {
return super.listPartsAsync(bucketName, region, objectName, maxParts, partNumberMarker, uploadId, extraHeaders, extraQueryParams).get();
}
}
package net.junnan.religious.base.api.form.common.Sdk;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* @title: FileCheckAndMergeForm
* @Author CaoJun
* @Date: 2025/5/7 上午11:27
* @Version 1.0
*/
@Data
public class FileCheckAndMergeForm {
@NotBlank(message = "bucket 不可为空")
private String bucket;
@NotBlank(message = "文件的MD5值不可为空")
private String md5;
}
package net.junnan.religious.base.api.form.religious.ShardFilesUpload;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
* 文件上传信息,查询 redis 后的返回信息
*/
@Data
@Accessors(chain = true)
public class FileUploadInfo {
@NotBlank(message = "md5 不能为空")
private String md5;
private String uploadId;
@NotBlank(message = "文件名不能为空")
private String originFileName;
// 仅秒传会有值
private String url;
// 后端使用
private String object;
private String type;
@NotNull(message = "文件大小不能为空")
private Long size;
@NotNull(message = "分片数量不能为空")
private Integer chunkCount;
@NotNull(message = "分片大小不能为空")
private Long chunkSize;
private String contentType;
// listParts 从 1 开始,前端需要上传的分片索引+1
private List<Integer> listParts;
@NotBlank(message = "bucket 不可为空")
private String bucket;
}
package net.junnan.religious.base.api.util;
import cn.hutool.core.util.IdUtil;
import com.google.common.collect.HashMultimap;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Part;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.junnan.religious.base.api.config.CustomMinioClient;
import net.junnan.religious.base.api.form.religious.ShardFilesUpload.FileUploadInfo;
import net.junnan.religious.base.api.form.religious.ShardFilesUpload.UploadUrlsVO;
import net.junnan.religious.base.api.properties.MyProperties;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Slf4j
@Component
public class MinioUtil {
private CustomMinioClient customMinioClient;
@Resource
MyProperties myProperties;
// spring自动注入会失败
@PostConstruct
public void init() {
MyProperties.Minio minio = myProperties.getStorage().getMinio();
MinioAsyncClient minioClient = MinioAsyncClient.builder()
.endpoint(String.format("%s://%s", minio.getProtocol(), minio.getEndPoint()))
.credentials(minio.getAccessKey(), minio.getSecretKey())
.build();
customMinioClient = new CustomMinioClient(minioClient);
}
/**
* 获取 Minio 中已经上传的分片文件
* @param object 文件名称
* @param uploadId 上传的文件id(由 minio 生成)
* @return List<Integer>
*/
@SneakyThrows
public List<Integer> getListParts(String object, String uploadId,String bucketName) {
List<Part> parts = getParts(object, uploadId,bucketName);
return parts.stream()
.map(Part::partNumber)
.collect(Collectors.toList());
}
/**
* 单文件签名上传
* @param object 文件名称(uuid 格式)
* @return UploadUrlsVO
*/
public UploadUrlsVO getUploadObjectUrl(String contentType, String object,String bucketName) throws Exception {
try {
UploadUrlsVO urlsVO = new UploadUrlsVO();
List<String> urlList = new ArrayList<>();
// 主要是针对图片,若需要通过浏览器直接查看,而不是下载,需要指定对应的 content-type
HashMultimap<String, String> headers = HashMultimap.create();
if (contentType == null || contentType.equals("")) {
contentType = "application/octet-stream";
}
headers.put("Content-Type", contentType);
String uploadId = IdUtil.simpleUUID();
Map<String, String> reqParams = new HashMap<>();
reqParams.put("uploadId", uploadId);
String url = customMinioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
.method(Method.PUT)
.bucket(bucketName)
.object(object)
.extraHeaders(headers)
.extraQueryParams(reqParams)
.expiry(1, TimeUnit.DAYS)
.build());
urlList.add(url);
urlsVO.setUploadId(uploadId).setUrls(urlList);
return urlsVO;
} catch (Exception e) {
throw new Exception(e);
}
}
/**
* 初始化分片上传
* @param fileUploadInfo 前端传入的文件信息
* @param object object
* @return UploadUrlsVO
*/
public UploadUrlsVO initMultiPartUpload(FileUploadInfo fileUploadInfo, String object,String bucketName) throws Exception {
Integer chunkCount = fileUploadInfo.getChunkCount();
String contentType = fileUploadInfo.getContentType();
String uploadId = fileUploadInfo.getUploadId();
UploadUrlsVO urlsVO = new UploadUrlsVO();
try {
HashMultimap<String, String> headers = HashMultimap.create();
if (contentType == null || contentType.equals("")) {
contentType = "application/octet-stream";
}
headers.put("Content-Type", contentType);
// 如果初始化时有 uploadId,说明是断点续传,不能重新生成 uploadId
if (fileUploadInfo.getUploadId() == null || fileUploadInfo.getUploadId().equals("")) {
uploadId = customMinioClient.initMultiPartUpload(bucketName, null, object, headers, null);
}
urlsVO.setUploadId(uploadId);
List<String> partList = new ArrayList<>();
Map<String, String> reqParams = new HashMap<>();
reqParams.put("uploadId", uploadId);
for (int i = 1; i <= chunkCount; i++) {
reqParams.put("partNumber", String.valueOf(i));
String uploadUrl = customMinioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
.method(Method.PUT)
.bucket(bucketName)
.object(object)
.expiry(1, TimeUnit.DAYS)
.extraQueryParams(reqParams)
.build());
partList.add(uploadUrl);
}
urlsVO.setUrls(partList);
return urlsVO;
} catch (Exception e) {
throw new Exception(e);
}
}
/**
* 合并文件
* @param object object
* @param uploadId uploadUd
*/
@SneakyThrows
public boolean mergeMultipartUpload(String object, String uploadId,String bucketName) {
// 获取所有分片
List<Part> partsList = getParts(object, uploadId,bucketName);
Part[] parts = new Part[partsList.size()];
int partNumber = 1;
for (Part part : partsList) {
parts[partNumber - 1] = new Part(partNumber, part.etag());
partNumber++;
}
// 合并分片
customMinioClient.mergeMultipartUpload(bucketName, null, object, uploadId, parts, null, null);
return true;
}
@NotNull
private List<Part> getParts(String object, String uploadId,String bucketName) throws Exception {
int partNumberMarker = 0;
boolean isTruncated = true;
List<Part> parts = new ArrayList<>();
while(isTruncated){
ListPartsResponse partResult = customMinioClient.listMultipart(bucketName, null, object, 1000, partNumberMarker, uploadId, null, null);
parts.addAll(partResult.result().partList());
// 检查是否还有更多分片
isTruncated = partResult.result().isTruncated();
if (isTruncated) {
// 更新partNumberMarker以获取下一页的分片数据
partNumberMarker = partResult.result().nextPartNumberMarker();
}
}
return parts;
}
}
package net.junnan.religious.base.api.util;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@Component
public class RedisUtil {
@Resource
private RedisTemplate redisTemplate;
/**
* 指定缓存失效时间
* @param key 键
* @param time 时间(秒)
* @return
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据key 获取过期时间
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判断key是否存在
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
//============================String=============================
/**
* 普通缓存获取
* @param key 键
* @return 值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存放入并设置时间
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存放入并设置时间和单位
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @param unit 单位
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time, TimeUnit unit) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, unit);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 递增
* @param key 键
* @param delta 要增加几(大于0)
* @return
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递减
* @param key 键
* @param delta 要减少几(小于0)
* @return
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
//================================Map=================================
/**
* HashGet
* @param key 键 不能为null
* @param item 项 不能为null
* @return 值
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取hashKey对应的所有键值
* @param key 键
* @return 对应的多个键值
*/
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* HashSet
* @param key 键
* @param map 对应多个键值
* @return true 成功 false 失败
*/
public boolean hmset(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* HashSet 并设置时间
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
* @return true成功 false失败
*/
public boolean hmset(String key, Map<String, Object> map, long time) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
* @param key 键
* @param item 项
* @param value 值
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
* @param key 键
* @param item 项
* @param value 值
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value, long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除hash表中的值
* @param key 键 不能为null
* @param item 项 可以使多个 不能为null
*/
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}
/**
* 判断hash表中是否有该项的值
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
* @param key 键
* @param item 项
* @param by 要增加几(大于0)
* @return
*/
public double hincr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* hash递减
* @param key 键
* @param item 项
* @param by 要减少记(小于0)
* @return
*/
public double hdecr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
}
//============================set=============================
/**
* 根据key获取Set中的所有值
* @param key 键
* @return
*/
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 根据value从一个set中查询,是否存在
* @param key 键
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将数据放入set缓存
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 将set数据放入缓存
* @param key 键
* @param time 时间(秒)
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSetAndTime(String key, long time, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0) expire(key, time);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取set缓存的长度
* @param key 键
* @return
*/
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 移除值为value的
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
//===============================list=================================
/**
* 获取list缓存的内容
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
* @return
*/
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取list缓存的长度
* @param key 键
* @return
*/
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 通过索引 获取list中的值
* @param key 键
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
* @return
*/
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 将list放入缓存
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0) expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0) expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据索引修改list中的某条数据
* @param key 键
* @param index 索引
* @param value 值
* @return
*/
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 移除N个值为value
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lRemove(String key, long count, Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
//================有序集合 sort set===================
/**
* 有序set添加元素
* @param key
* @param value
* @param score
* @return
*/
public boolean zSet(String key, Object value, double score) {
return redisTemplate.opsForZSet().add(key, value, score);
}
public long batchZSet(String key, Set<ZSetOperations.TypedTuple> typles) {
return redisTemplate.opsForZSet().add(key, typles);
}
public void zIncrementScore(String key, Object value, long delta) {
redisTemplate.opsForZSet().incrementScore(key, value, delta);
}
public void zUnionAndStore(String key, Collection otherKeys, String destKey) {
redisTemplate.opsForZSet().unionAndStore(key, otherKeys, destKey);
}
/**
* 获取zset数量
* @param key
* @param value
* @return
*/
public long getZsetScore(String key, Object value) {
Double score = redisTemplate.opsForZSet().score(key, value);
if (score == null) {
return 0;
} else {
return score.longValue();
}
}
/**
* 获取有序集 key 中成员 member 的排名 。
* 其中有序集成员按 score 值递减 (从大到小) 排序。
* @param key
* @param start
* @param end
* @return
*/
public Set<ZSetOperations.TypedTuple> getZSetRank(String key, long start, long end) {
return redisTemplate.opsForZSet().reverseRangeWithScores(key, start, end);
}
}
package net.junnan.religious.base.api.entity.common; import lombok.Data; import java.io.Serializable; /** * @title: ShardFilesUpload * @Author CaoJun * @Date: 2025/5/6 上午11:14 * @Version 1.0 */ @Data public class ShardFilesUpload implements Serializable { private static final long serialVersionUID = 1L; private String id ; /** 文件上传id */ private String uploadId ; /** 文件计算md5 */ private String md5 ; /** 文件访问地址 */ private String url ; /** 存储桶 */ private String bucket ; /** minio中文件名 */ private String object ; /** 原始文件名 */ private String originFileName ; /** 文件大小 */ private Long size ; /** 文件类型 */ private String type ; /** 分片大小 */ private Long chunkSize ; /** 分片数量 */ private Integer chunkCount ; /** 是否删除 */ private Integer isDelete ; /** 创建时间 */ private Long createTime ; }
package net.junnan.religious.base.api.mapper.common; import net.junnan.religious.base.api.entity.common.ShardFilesUpload; import org.apache.ibatis.annotations.Mapper; /** * @title: ShardFilesUploadMapper * @Author CaoJun * @Date: 2025/5/6 上午11:13 * @Version 1.0 */ @Mapper public interface ShardFilesUploadMapper{ /** * 新增数据 * * @param shardFilesUpload 实例对象 * @return 影响行数 */ int insert(ShardFilesUpload shardFilesUpload); ShardFilesUpload selectShardFilesUploadByMd5(String md5,String bucket); }
@SneakyThrows @Override public Result checkFileByMd5(FileCheckAndMergeForm fileCheckAndMergeForm) { String selector = myProperties.getStorage().getSelector(); switch (selector) { case "minio": return minioStorage.checkFileByMd5(fileCheckAndMergeForm); case "aliyun": throw new Exception("aliyunStorage的方法缺失"); default: throw new Exception("不支持的 my.storage.selector"); } } @SneakyThrows @Override public Result initMultipartUpload(FileUploadInfo fileUploadInfo) { String selector = myProperties.getStorage().getSelector(); switch (selector) { case "minio": return minioStorage.initMultipartUpload(fileUploadInfo); case "aliyun": throw new Exception("aliyunStorage的方法缺失"); default: throw new Exception("不支持的 my.storage.selector"); } } @SneakyThrows @Override public Result mergeMultipartUpload(FileCheckAndMergeForm fileCheckAndMergeForm) { String selector = myProperties.getStorage().getSelector(); switch (selector) { case "minio": return minioStorage.mergeMultipartUpload(fileCheckAndMergeForm); case "aliyun": throw new Exception("aliyunStorage的方法缺失"); default: throw new Exception("不支持的 my.storage.selector"); } }
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="net.junnan.religious.base.api.mapper.common.ShardFilesUploadMapper"> <resultMap type="net.junnan.religious.base.api.entity.common.ShardFilesUpload" id="BaseResultMap"> <result property="id" column="id" jdbcType="VARCHAR"/> <result property="uploadId" column="upload_id" jdbcType="VARCHAR"/> <result property="md5" column="md5" jdbcType="VARCHAR"/> <result property="url" column="url" jdbcType="VARCHAR"/> <result property="bucket" column="bucket" jdbcType="VARCHAR"/> <result property="object" column="object" jdbcType="VARCHAR"/> <result property="originFileName" column="origin_file_name" jdbcType="VARCHAR"/> <result property="size" column="size" jdbcType="BIGINT"/> <result property="type" column="type" jdbcType="VARCHAR"/> <result property="chunkSize" column="chunk_size" jdbcType="BIGINT"/> <result property="chunkCount" column="chunk_count" jdbcType="INTEGER"/> <result property="isDelete" column="is_delete" jdbcType="INTEGER"/> <result property="createTime" column="create_time" jdbcType="BIGINT"/> </resultMap> <select id="selectShardFilesUploadByMd5" parameterType="String" resultMap="BaseResultMap"> select id, upload_id, md5, url, bucket, object, origin_file_name, size, type, chunk_size, chunk_count, is_delete, create_time from RELIGIOUS.SHARD_FILES_UPLOAD WHERE md5 = #{md5} and bucket = #{bucket} and is_delete =0 </select> <!--新增数据--> <insert id="insert" keyProperty="id" useGeneratedKeys="true"> insert into RELIGIOUS.SHARD_FILES_UPLOAD(id, upload_id, md5, url, bucket, object, origin_file_name, size, type, chunk_size, chunk_count, is_delete, create_time) values (#{id}, #{uploadId}, #{md5}, #{url}, #{bucket}, #{object}, #{originFileName}, #{size}, #{type}, #{chunkSize}, #{chunkCount}, #{isDelete}, #{createTime}) </insert> </mapper>
package net.junnan.religious.base.api.form.religious.ShardFilesUpload; import lombok.Data; import lombok.experimental.Accessors; import java.util.List; /** * 返回文件生成的分片上传地址 */ @Data @Accessors(chain = true) public class UploadUrlsVO { private String uploadId; private List<String> urls; }
package net.junnan.religious.base.api.util;
import com.alibaba.fastjson.JSON;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
public class BeanUtil {
public static <T> T copyFrom(Object obj, Class<T> clazz) {
try {
if (obj == null) {
return null;
}
T bean = clazz.getDeclaredConstructor().newInstance();
String json = JSON.toJSONString(obj);
return JSON.parseObject(json, (Type) bean.getClass());
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
return null;
}
}
}