官方文档:MinIO Quickstart Guide| Minio中文文档
1、简介
Minio 是一个基于Apache License v2.0开源协议的对象存储服务
。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化
的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。
Minio是一个非常轻量的服务,可以很简单的和其他应用的结合,类似 NodeJS, Redis 或者 MySQL。
2、容器安装
将独立的 MinIO 服务器作为容器运行,独立的 MinIO 服务器最适合早期开发和评估。 某些功能,例如版本控制、对象锁定和存储桶复制 需要使用擦除编码分布式部署 MinIO。
2.1、Docker
docker run -p 9000:9000 \
--name minio1 \
-v /mnt/data:/data \
-e "MINIO_ROOT_USER=AKIAIOSFODNN7EXAMPLE" \
-e "MINIO_ROOT_PASSWORD=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" \
minio/minio server /data
# 命令详解
# -e MINIO_ROOT_USER 指定用户名
# -e MINIO_ROOT_PASSWORD 指定密码
# -v 挂载目录,持久化minio目录
2.2、Linux
# 下载
wget https://dl.min.io/server/minio/release/linux-amd64/minio
# 加权
chmod +x minio
# 启动 你可以更换后面的目录为你自己需要的目录
./minio server /data
2.3、windows(详细)
(1)下载:
http://dl.minio.org.cn/server/minio/release/windows-amd64/minio.exe
(2)启动:
minio.exe server D:\
将“D:\”替换为您希望 MinIO 存储数据的驱动器或目录的路径。 您必须将终端或 powershell 目录更改为 minio.exe
可执行文件的位置,或将该目录的路径添加到系统 $PATH
中。
注:也就是需要先创建好存储文件的目录,如我的启动命令为:minio.exe server D:\data,保存为bat文件,双击启动即可。
上图就启动成功了,警告先不管,访问http://192.168.0.123:9000即可。之前创建的文件也有初始化内容了,还没有文件。
3、使用说明
(1)访问地址:
http://{ip}:{port}/minio/ 或 http://{ip}:{port}
默认的 root 凭据 minioadmin:minioadmin。
这样就可以使用浏览器来创建桶、上传对象以及浏览 MinIO 服务器的内容了。
(2)创建buckets
(3)上传文件
去看一下之前的目录,文件也有了
4、JAVA使用minio的方法
上面的是直接才界面控制台进行创建桶、上传文件、删除文件等操作,下面来使用java代码操作,写个简单demo,演示连接到一个对象存储服务,创建一个存储桶并上传一个文件到该桶中。
(1)添加依赖
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>7.1.0</version>
</dependency>
(2)参数配置
需要有存储服务的三个参数才能连接到该服务
参数 | 说明 |
---|---|
Endpoint | 对象存储服务的URL |
Access Key | Access key就像用户ID,可以唯一标识你的账户。 |
Secret Key | Secret key是你账户的密码。 |
我这里配置在yaml文件中的:
(3)编写minio配置信息
package com.xxx.xxx.common.config;
import com.xxx.xxx.common.exception.MinioException;
import io.minio.BucketExistsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.SetBucketPolicyArgs;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
/**
* minio自动配置.
*
* @author Mei rx
* @since 2021/08/05
*/
@Configuration
@ConfigurationProperties(prefix = "minio")
@ConditionalOnClass({MinioClient.class})
@ConditionalOnProperty({"minio.endpoint"})
public class MinioAutoConfiguration {
@Autowired
private Environment environment;
public MinioAutoConfiguration() {
}
@Bean
public MinioClient minioClient() {
String endpoint = this.environment.getProperty("minio.endpoint");
String accessKey = this.environment.getProperty("minio.accessKey");
String secretKey = this.environment.getProperty("minio.secretKey");
String bucketName = this.environment.getProperty("minio.bucketName");
if (endpoint != null && !"".equals(endpoint)) {
if (accessKey != null && !"".equals(accessKey)) {
if (secretKey != null && !"".equals(secretKey)) {
if (bucketName != null && !"".equals(bucketName)) {
MinioClient minioClient = MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build();
this.makeBucket(minioClient, bucketName);
return minioClient;
} else {
throw new MinioException("存储桶名称未在application.yml配置!");
}
} else {
throw new MinioException("Minio密码未在application.yml配置!");
}
} else {
throw new MinioException("Minio用户名未在application.yml配置!");
}
} else {
throw new MinioException("Minio的URL未在application.yml配置!");
}
}
private void makeBucket(MinioClient minioClient, String bucketName) {
try {
boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
if (!isExist) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
String policyJson = "Bucket already exists.";
minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucketName).config(policyJson).build());
}
} catch (Exception e) {
throw new MinioException("创建minio存储桶异常", e);
}
}
}
(4)编写上传代码
public static String uploadFile(InputStream inputStream, String objectName, String fileName) {
if (StringUtil.isNotBlank(fileName)) {
objectName = objectName + "/" + fileName;
}
try {
if (objectName != null && !"".equals(objectName)) {
try {
minioUtil.minioClient.putObject(PutObjectArgs
.builder()
.bucket(minioUtil.bucketName)
.object(objectName)
.stream(inputStream, inputStream.available(), -1)
.build());
log.info("文件上传成功!");
} catch (Exception var4) {
log.error("添加存储对象异常", var4);
throw new MinioException("添加存储对象异常", var4);
}
} else {
throw new MinioException("存储对象名称objectName不能为空!");
}
log.info("文件上传成功!");
return minioUtil.getUrl(objectName);
} catch (Exception var4) {
var4.printStackTrace();
log.error("上传发生错误: {}!", var4.getMessage());
return var4.getMessage();
}
}
(5)调用上传方法
/**
* 上传材料
*/
@PostMapping("/upload")
@ApiOperation("上传文件")
public String upload(
@ApiParam(value = "文件", example = "11.jpg", required = true)
@RequestPart(value = "file") MultipartFile file) throws IOException {
log.info("uploadFile:上传文件[file:{}]", file);
return MinioUtil.uploadFile(file.getInputStream(), "my-file", file.getOriginalFilename());
}
(6)测试
调用接口上传文件成功,返回一个可访问的url,访问一下
注意:如果出现如下提示,很有可能是buckets的权限问题,修改一下权限即可
4、全部代码
配置类:
package com.guodi.zjjggl.common.config;
import com.guodi.zjjggl.common.exception.MinioException;
import io.minio.BucketExistsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.SetBucketPolicyArgs;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
/**
* minio自动配置.
*
* @author 刘雨昕
* @since 2021/09/24
*/
@Configuration
@ConfigurationProperties(prefix = "minio")
@ConditionalOnClass({MinioClient.class})
@ConditionalOnProperty({"minio.endpoint"})
public class MinioAutoConfiguration {
@Autowired
private Environment environment;
public MinioAutoConfiguration() {
}
@Bean
public MinioClient minioClient() {
String endpoint = this.environment.getProperty("minio.endpoint");
String accessKey = this.environment.getProperty("minio.accessKey");
String secretKey = this.environment.getProperty("minio.secretKey");
String bucketName = this.environment.getProperty("minio.bucketName");
if (endpoint != null && !"".equals(endpoint)) {
if (accessKey != null && !"".equals(accessKey)) {
if (secretKey != null && !"".equals(secretKey)) {
if (bucketName != null && !"".equals(bucketName)) {
MinioClient minioClient = MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build();
this.makeBucket(minioClient, bucketName);
return minioClient;
} else {
throw new MinioException("存储桶名称未在application.yml配置!");
}
} else {
throw new MinioException("Minio密码未在application.yml配置!");
}
} else {
throw new MinioException("Minio用户名未在application.yml配置!");
}
} else {
throw new MinioException("Minio的URL未在application.yml配置!");
}
}
private void makeBucket(MinioClient minioClient, String bucketName) {
try {
boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
if (!isExist) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
String policyJson = "Bucket already exists.";
minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucketName).config(policyJson).build());
}
} catch (Exception e) {
throw new MinioException("创建minio存储桶异常", e);
}
}
}
工具类:
package com.xxx.xxx.util;
import com.xxx.core.minio.exception.MinioException;
import com.xxx.core.tool.utils.Charsets;
import com.xxx.core.tool.utils.DateUtil;
import com.xxx.core.tool.utils.StringUtil;
import io.minio.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* minio存储对象工具类.
*
* @author 刘雨昕
* @since 2021/09/24
*/
@Component
@Slf4j
public class MinioUtil {
@Value("${minio.bucketName}")
private String bucketName;
@Resource
private MinioClient minioClient;
private static MinioUtil minioUtil;
public MinioUtil() {
}
@PostConstruct
public void init() {
minioUtil = this;
}
public static String uploadFile(InputStream inputStream, String objectName, String fileName) {
if (StringUtil.isNotBlank(fileName)) {
objectName = objectName + "/" + fileName;
}
try {
if (objectName != null && !"".equals(objectName)) {
try {
minioUtil.minioClient.putObject(PutObjectArgs
.builder()
.bucket(minioUtil.bucketName)
.object(objectName)
.stream(inputStream, inputStream.available(), -1)
.build());
log.info("文件上传成功!");
} catch (Exception e) {
log.error("添加存储对象异常", e);
throw new MinioException("添加存储对象异常", e);
}
} else {
throw new MinioException("存储对象名称objectName不能为空!");
}
log.info("文件上传成功!");
return minioUtil.getUrl(objectName);
} catch (Exception ex) {
ex.printStackTrace();
log.error("上传发生错误: {}!", ex.getMessage());
return ex.getMessage();
}
}
public static InputStream download(String fileUrl) {
try {
fileUrl = fileUrl.substring(fileUrl.indexOf(minioUtil.bucketName) + minioUtil.bucketName.length());
InputStream inputStream = minioUtil.get(fileUrl);
log.info("下载成功");
return inputStream;
} catch (Exception e) {
e.printStackTrace();
log.error("下载发生错误: {}!", e.getMessage());
return null;
}
}
public static void batchDownload(List<String> fileUrlList, String zipName, HttpServletResponse httpServletResponse) {
ZipOutputStream zos;
ZipEntry zipEntry;
byte[] buff = new byte[1024];
if (fileUrlList != null && !fileUrlList.isEmpty()) {
try {
if (StringUtil.isEmpty(zipName)) {
zipName = "批量下载" + DateUtil.format(new Date(), "yyyyMMddHHmmss");
}
//清除缓冲区中存在的所有数据以及状态代码和标头。如果已提交响应,则此方法将抛出IllegalStateException
httpServletResponse.reset();
//Content-Disposition为属性名,attachment以附件方式下载,filename下载文件默认名字
httpServletResponse.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(zipName, Charsets.UTF_8.name()) + ".zip");
//另存为弹框加载
httpServletResponse.setContentType("application/x-msdownload");
httpServletResponse.setCharacterEncoding("utf-8");
zos = new ZipOutputStream(httpServletResponse.getOutputStream());
for (String fileUrl : fileUrlList) {
//获取minio对应路径文件流(从数据库中获取的url是编码的,这里要先进行解码,不然minioClient.getObject()方法里面会再进行一次编码,就获取不到对象)
String url = URLDecoder.decode(fileUrl,Charsets.UTF_8.name());
String downloadUrl = url.substring(url.indexOf(minioUtil.bucketName) + minioUtil.bucketName.length());
InputStream inputStream = minioUtil.get(downloadUrl);
String fileName = url.substring(url.lastIndexOf("/"), url.lastIndexOf("."));
zipEntry = new ZipEntry(fileName + url.substring(url.lastIndexOf(".")));
zos.putNextEntry(zipEntry);
int length;
while ((length = inputStream.read(buff)) > 0) {
zos.write(buff, 0, length);
}
}
log.info("批量下载成功!");
zos.close();
} catch (IOException ioException) {
ioException.printStackTrace();
log.error("批量下载发生错误! msg=" + ioException.getMessage());
} finally {
}
} else {
log.error("批量下载发生错误,文件访问路径集合不能为空!");
}
}
public static void removeFile(String fileUrl) {
try {
String downloadUrl = fileUrl.substring(fileUrl.indexOf(minioUtil.bucketName) + minioUtil.bucketName.length());
minioUtil.rm(downloadUrl);
log.info("文件删除成功!");
} catch (Exception e) {
e.printStackTrace();
log.error("文件删除失败! msg=" + e.getMessage());
}
}
public static void batchRemoveFile(List<String> fileUrlList) {
if (fileUrlList != null && !fileUrlList.isEmpty()) {
try {
for (String fileUrl : fileUrlList) {
String downloadUrl = fileUrl.substring(fileUrl.indexOf(minioUtil.bucketName) + minioUtil.bucketName.length());
minioUtil.rm(downloadUrl);
}
log.info("文件批量删除成功!");
} catch (Exception e) {
e.printStackTrace();
log.error("文件批量删除失败! msg=" + e.getMessage());
}
}
}
public InputStream get(String objectName) {
InputStream inputStream;
try {
inputStream = this.minioClient.getObject(GetObjectArgs.builder().bucket(this.bucketName).object(objectName).build());
return inputStream;
} catch (Exception e) {
log.error("读取存储对象异常", e);
throw new MinioException("读取存储对象异常", e);
}
}
public void download(String objectName, String fileName) {
try {
this.minioClient.downloadObject(DownloadObjectArgs.builder().bucket(this.bucketName).object(objectName).filename(fileName).build());
} catch (Exception e) {
log.error("下载发生错误:[{}]", e.getMessage());
throw new MinioException("下载发生错误", e);
}
}
public String getUrl(String objectName) {
try {
return this.minioClient.getObjectUrl(this.bucketName, objectName);
} catch (Exception e) {
log.error("获取存储对象url异常", e);
throw new MinioException("获取存储对象url异常", e);
}
}
public void rm(String objectName) {
try {
this.minioClient.removeObject(RemoveObjectArgs.builder().bucket(this.bucketName).object(objectName).build());
} catch (Exception e) {
log.error("删除存储对象异常", e);
throw new MinioException("删除存储对象异常", e);
}
}
public void rmBatch(Collection<String> objectNames) {
if (!CollectionUtils.isEmpty(objectNames)) {
objectNames.forEach(this::rm);
}
}
}
异常类:
package com.guodi.zjjggl.common.exception;
/**
* minio存储异常.
*
* @author 刘雨昕
* @since 2021/09/24
*/
public class MinioException extends RuntimeException {
private static final long serialVersionUID = 1L;
protected String code = "400900";
protected String message;
public MinioException(String message) {
this.message = message;
}
public MinioException(String code, String message) {
this.code = code;
this.message = message;
}
public MinioException(String code, String message, Throwable cause) {
this.code = code;
message = message + "'\\n" + cause.getMessage();
this.message = message;
}
public MinioException(String message, Throwable cause) {
message = message + "'\\n" + cause.getMessage();
this.message = message;
}
public String getCode() {
return this.code;
}
public void setCode(String code) {
this.code = code;
}
@Override
public String getMessage() {
return this.message;
}
public void setMessage(String message) {
this.message = message;
}
}
日期工具(可以用自己的日期工具,没有的可以用我的):
package com.xjjj.xjjjjt.uitls;
import jakarta.validation.constraints.NotNull;
import org.springframework.lang.NonNull;
import org.springframework.util.StringUtils;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalUnit;
import java.util.*;
/**
* 日期工具类
*
* @author 刘雨昕
* @since 2021/8/31
*/
public class DateUtil {
/**
* 日期格式器--年
*/
public static final String DATE_FORMAT_YEAR = "yyyy";
/**
* 日期格式器
*/
public static final String DATE_FORMAT_MONTH = "yyyy-MM";
/**
* 日期格式器
*/
public static final String DATE_FORMAT_MONTH_DAY = "MM-dd";
/**
* 日期格式器
*/
public static final String DATE_FORMAT = "yyyy-MM-dd";
/**
* 日期格式器
*/
public static final String DATE_FORMAT_HH = "yyyy-MM-dd HH";
/**
* 日期格式器
*/
public static final String DATE_FORMAT_MM = "yyyy-MM-dd HH:mm";
/**
* 时间格式器
*/
public static final String TIME_FORMAT = "HH:mm:ss";
/**
* 日期时间格式器
*/
public static final String DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
/**
* 日期时间格式器
*/
public static final String DATETIME_FORMAT_1 = "HH:mm:ss 00:00:00";
/**
* 日期戳
*/
public static final String DATE_STAMP = "yyyyMMdd";
/**
* 时间戳
*/
public static final String TIME_STAMP = "HHmmssSSS";
/**
* 时间戳(精确到秒)
*/
public static final String TIME_STAMP_SECOND = "HHmmss";
/**
* 日期时间戳
*/
public static final String DATETIME_STAMP = "yyyyMMddHHmmssSSS";
/**
* 日期时间戳(精确到秒)
*/
public static final String DATETIME_STAMP_SECOND = "yyyyMMddHHmmss";
/**
* UTC时间戳
*/
public static final SimpleDateFormat UTC_TIME_STAMP_FORMAT =
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
/**
* UTC时间格式化器
*/
public static final SimpleDateFormat UTC_FORMAT =
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
/**
* GMT时间格式化器
*/
public static final SimpleDateFormat GMT_FORMAT =
new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
public static final Integer HALF_YEAR = 6;
static {
UTC_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
UTC_TIME_STAMP_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
GMT_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT"));
}
/**
* 天
*/
public static final String DAY_STR = "天";
/**
* 小时
*/
public static final String HOUR_STR = "小时";
/**
* 分钟
*/
public static final String MINUTE_STR = "分钟";
/**
* 将字符串解析成yyyy-MM-dd的日期
*
* @param value the value
* @return the date
*/
public static Date parseDate(String value) {
try {
return new SimpleDateFormat(DATE_FORMAT, Locale.CHINA).parse(value);
} catch (ParseException e) {
return new Date();
}
}
/**
* 将字符串解析成HH:mm:ss的时间
*
* @param value the value
* @return the date
*/
public static Date parseTime(String value) {
try {
return new SimpleDateFormat(TIME_FORMAT, Locale.CHINA).parse(value);
} catch (ParseException e) {
return null;
}
}
/**
* 将字符串解析成yyyy-MM-dd HH:mm:ss的日期时间
*
* @param value the value
* @return the date
*/
public static Date parseDateTime(String value) {
if (!StringUtils.hasText(value)) {
return null;
}
try {
return new SimpleDateFormat(DATETIME_FORMAT, Locale.CHINA).parse(value);
} catch (ParseException e) {
return null;
}
}
/**
* 将字符串解析成自定义格式的日期时间
*
* @param value the value
* @param format the format
* @return the date
*/
public static Date parseDateTime(String value, String format) {
try {
return new SimpleDateFormat(format, Locale.CHINA).parse(value);
} catch (ParseException e) {
return null;
}
}
/**
* 将字符串解析成yyyyMMddHHmmssSSS的日期时间
*
* @param value the value
* @return the date
*/
public static Date parseDateTimeStamp(String value) {
try {
return new SimpleDateFormat(DATETIME_STAMP, Locale.CHINA).parse(value);
} catch (ParseException e) {
return null;
}
}
/**
* 将字符串解析成yyyyMMddHHmmss的日期时间
*
* @param value the value
* @return the date
*/
public static Date parseDateTimeSecond(String value) {
try {
return new SimpleDateFormat(DATETIME_STAMP_SECOND, Locale.CHINA).parse(value);
} catch (ParseException e) {
return null;
}
}
/**
* 将时间解析成yyyy-MM-dd的字符串
*
* @param date the date
* @return the string
*/
public static String formatDate(Date date) {
if (date == null) {
return "";
}
return new SimpleDateFormat(DATE_FORMAT, Locale.CHINA).format(date);
}
/**
* 将时间解析成yyyy的字符串
*
* @param date the date
* @return the string
*/
public static String formatDateToYear(Date date) {
if (date == null) {
return "";
}
return new SimpleDateFormat(DATE_FORMAT_YEAR, Locale.CHINA).format(date);
}
/**
* 将日期解析成HH:mm:ss的字符串
*
* @param date the date
* @return the string
*/
public static String formatTime(Date date) {
if (date == null) {
return "";
}
return new SimpleDateFormat(TIME_FORMAT, Locale.CHINA).format(date);
}
/**
* 将日期时间解析成yyyy-MM-dd HH:mm:ss的字符串
*
* @param date the date
* @return the string
*/
public static String formatDateTime(Date date) {
if (date == null) {
return "";
}
return new SimpleDateFormat(DATETIME_FORMAT, Locale.CHINA).format(date);
}
/**
* 将日期解析成yyyyMMddHHmmssSSS的字符串
*
* @param date the date
* @return the string
*/
public static String formatDateTimestamp(Date date) {
if (date == null) {
return "";
}
return new SimpleDateFormat(DATETIME_STAMP, Locale.CHINA).format(date);
}
/**
* 将日期解析成yyyyMMddHHmmss的字符串
*
* @param date the date
* @return the string
*/
public static String formatDateTimeSecond(Date date) {
if (date == null) {
return "";
}
return new SimpleDateFormat(DATETIME_STAMP_SECOND, Locale.CHINA).format(date);
}
/**
* 将日期解析成yyyyMMdd的字符串
*
* @param date the date
* @return the string
*/
public static String formatDatestamp(Date date) {
if (date == null) {
return "";
}
return new SimpleDateFormat(DATE_STAMP, Locale.CHINA).format(date);
}
/**
* 将日期解析成HHmmssSSS的字符串
*
* @param date the date
* @return the string
*/
public static String formatTimestamp(Date date) {
if (date == null) {
return "";
}
return new SimpleDateFormat(TIME_STAMP, Locale.CHINA).format(date);
}
/**
* 将日期解析成yyyy-MM-dd HH的字符串
*
* @param date the date
* @return the string
*/
public static String formatDateHH(Date date) {
if (date == null) {
return "";
}
return new SimpleDateFormat(DATE_FORMAT_HH, Locale.CHINA).format(date);
}
/**
* 将日期解析成yyyy-MM-dd HH:mm的字符串
*
* @param date the date
* @return the string
*/
public static String formatDateMM(Date date) {
if (date == null) {
return "";
}
return new SimpleDateFormat(DATE_FORMAT_MM, Locale.CHINA).format(date);
}
/**
* 将日期解析成yyyy-MM的字符串
*
* @param date the date
* @return the string
*/
public static String formatYearMonth(Date date) {
if (date == null) {
return "";
}
return new SimpleDateFormat(DATE_FORMAT_MONTH, Locale.CHINA).format(date);
}
/**
* 将日期解析成MM-dd的字符串
*
* @param date the date
* @return the string
*/
public static String formatMonthDay(Date date) {
if (date == null) {
return "";
}
return new SimpleDateFormat(DATE_FORMAT_MONTH_DAY, Locale.CHINA).format(date);
}
/**
* 获取当天的开始时间, 如:2020-06-24 00:00:00
*
* @return the begin of date
*/
public static Date getBeginOfDate() {
return getBeginOfDate(null);
}
/**
* 获取当天的结束时间, 如:2020-06-24 23:59:59
*
* @return the end of date
*/
public static Date getEndOfDate() {
return getEndOfDate(null);
}
/**
* 获取指定日期的开始时间, 如:2020-06-25 00:00:00
*
* @param date the date
* @return the begin of date
*/
public static Date getBeginOfDate(Date date) {
Calendar calendar = Calendar.getInstance();
if (date != null) {
calendar.setTime(date);
}
return setDayToBegin(calendar).getTime();
}
/**
* 获取指定日期的结束时间, 如:2020-02-25 23:59:59
*
* @param date the date
* @return the end of date
*/
public static Date getEndOfDate(Date date) {
Calendar calendar = Calendar.getInstance();
if (date != null) {
calendar.setTime(date);
}
return setDayToEnd(calendar).getTime();
}
/**
* 获取当年天数.
*
* @return days
*/
public static int getDaysInYear() {
GregorianCalendar calendar = new GregorianCalendar();
return calendar.isLeapYear(calendar.get(Calendar.YEAR)) ? 366 : 365;
}
/**
* 格式化当前日期.
*
* @return date型时间 sys date of date
*/
public static Date getSysDateOfDate() {
return parseDateTime(formatDateTime(new Date()));
}
/**
* 获取指定日期.
*
* @param calendarType Calendar.DATE、Calendar.MONTH、Calendar.YEAR等
* @param num 对应日期类型对应的数量
* @return 指定date型时间 比如:离现在30天前的日期、获取上个月日期
*/
public static Date getDefineDate(int calendarType, int num) {
Calendar cal = Calendar.getInstance();
cal.add(calendarType, num);
return cal.getTime();
}
/**
* 获取天数差.
*
* @param specifiedDate 指定的时间,即需要比较的时间
* @param largeDate 到期的时间,即比较大的时间
* @return 天数差 计算指定时间与当前时间的天数差
*/
public static int getDaysDifference(Date specifiedDate, Date largeDate) {
Calendar cNow = Calendar.getInstance();
Calendar cReturnDate = Calendar.getInstance();
cNow.setTime(largeDate);
cReturnDate.setTime(specifiedDate);
setDayToBegin(cNow);
setDayToBegin(cReturnDate);
long todayMs = cNow.getTimeInMillis();
long returnMs = cReturnDate.getTimeInMillis();
long intervalMs = todayMs - returnMs;
return (int) (intervalMs / (1000 * 86400));
}
/**
* 比较日期.
*
* @param referenceDate 需要比较的时间 不能为空(null),需要正确的日期格式
* @param date 被比较的时间 为空(null)则为当前时间
* @param type 返回值类型,0为多少天,1为多少个月,2为多少年
* @return the int
* @throws ParseException the parse exception
*/
public static int compareDate(String referenceDate, String date, int type)
throws ParseException {
int n = 0;
String formatStyle = type == 1 ? DATE_FORMAT_MONTH : DATE_FORMAT;
String dateStr = date;
if (dateStr == null) {
dateStr = formatDate(new Date());
}
DateFormat df = new SimpleDateFormat(formatStyle);
Calendar c1 = Calendar.getInstance();
Calendar c2 = Calendar.getInstance();
c1.setTime(df.parse(referenceDate));
c2.setTime(df.parse(dateStr));
// 循环对比,直到相等,n 就是所要的结果
while (!c1.after(c2)) {
n++;
if (type == 1) {
// 比较月份,月份+1
c1.add(Calendar.MONTH, 1);
} else {
// 比较天数,日期+1
c1.add(Calendar.DATE, 1);
}
}
n = n - 1;
if (type == 2) {
n = n / 365;
}
return n;
}
/**
* 获取当前时间之前 /之后多少年/月/日的时间.
*
* @param format 日期格式
* @param variable 正数代表时间之后,负数代表时间之前
* @param field 单位,年,月,日
* @return 时间
* @see #getBeforeDays
*/
@Deprecated
public static String getDiffDate(String format, int variable, int field) {
Calendar c = Calendar.getInstance();
SimpleDateFormat formatter = new SimpleDateFormat(format, Locale.CHINA);
c.set(field, c.get(field) + variable);
return formatter.format(c.getTime());
}
/**
* 获取指定时间相加天数后的时间.
*
* @param date 传入相加时间
* @param field 时间格式
* @param amount 日期或时间的数量
* @return 相加后的时间 date
* @see #getAfterDays
*/
@Deprecated
public static Date dateAdd(Date date, int field, int amount) {
Calendar ca = Calendar.getInstance();
if (date != null) {
ca.setTime(date);
}
ca.add(field, amount);
return ca.getTime();
}
/**
* 获取当前时间相加天数的时间.
*
* @param field 时间格式
* @param amount 相加的时间天数
* @return 相加后的时间 date
*/
public static Date dateAdd(int field, int amount) {
return dateAdd(null, field, amount);
}
/**
* 时间范围
*/
public static class DateRange {
/**
* 开始时间
*/
private Date begin;
/**
* 结束时间
*/
private Date end;
/**
* Instantiates a new Date range.
*/
public DateRange() {
}
/**
* Instantiates a new Date range.
*
* @param begin the begin
* @param end the end
*/
public DateRange(Date begin, Date end) {
this.begin = begin;
this.end = end;
}
/**
* Gets begin.
*
* @return the begin
*/
public Date getBegin() {
return begin;
}
/**
* Gets end.
*
* @return the end
*/
public Date getEnd() {
return end;
}
}
/**
* 获取指定日期的当天时间范围,如(2020-12-09 00:00:00 2020-12-12 23:59:59)
*
* @param date 时间,为空表示当前时间
* @return 时间范围 day range
*/
public static DateRange getDayRange(Date date) {
DateRange range = new DateRange();
Calendar calendar = Calendar.getInstance();
if (date != null) {
calendar.setTime(date);
}
range.begin = setDayToBegin(calendar).getTime();
range.end = setDayToEnd(calendar).getTime();
return range;
}
/**
* 获取指定日期本周的时间范围,如(2020-12-14 00:00:00 2020-12-20 23:59:59)
*
* @param date 时间,为空表示当前时间
* @return 时间范围 week range
*/
public static DateRange getWeekRange(Date date) {
DateRange range = new DateRange();
Calendar calendar = Calendar.getInstance();
if (date != null) {
calendar.setTime(date);
}
calendar.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
range.begin = setDayToBegin(calendar).getTime();
calendar.set(Calendar.DAY_OF_YEAR, calendar.get(Calendar.DAY_OF_YEAR) + 6);
range.end = setDayToEnd(calendar).getTime();
return range;
}
/**
* 获取指定日期本月的时间范围,如(2020-12-01 00:00:00 2020-12-31 23:59:59)
*
* @param date 时间,为空表示当前时间
* @return 时间范围 month range
*/
public static DateRange getMonthRange(Date date) {
DateRange range = new DateRange();
Calendar calendar = Calendar.getInstance();
if (date != null) {
calendar.setTime(date);
}
// 获取第一天
calendar.set(Calendar.DAY_OF_MONTH, 1);
range.begin = setDayToBegin(calendar).getTime();
// 获取最后一天
calendar.set(Calendar.MONTH, calendar.get(Calendar.MONTH) + 1);
calendar.set(Calendar.DAY_OF_YEAR, calendar.get(Calendar.DAY_OF_YEAR) - 1);
range.end = setDayToEnd(calendar).getTime();
return range;
}
/**
* 获取指定日期本季度的时间范围,如(2020-10-01 00:00:00 2020-12-31 23:59:59)
*
* @param date 时间,为空表示当前时间
* @return 时间范围 season range
*/
public static DateRange getSeasonRange(Date date) {
DateRange range = new DateRange();
Calendar calendar = Calendar.getInstance();
if (date != null) {
calendar.setTime(date);
}
int month = calendar.get(Calendar.MONTH);
calendar.set(Calendar.MONTH, month - (month % 3));
calendar.set(Calendar.DAY_OF_MONTH, 1);
range.begin = setDayToBegin(calendar).getTime();
calendar.set(Calendar.MONTH, calendar.get(Calendar.MONTH) + 3);
calendar.set(Calendar.DAY_OF_YEAR, calendar.get(Calendar.DAY_OF_YEAR) - 1);
range.end = setDayToEnd(calendar).getTime();
return range;
}
/**
* 获取指定日期本半年的时间范围,如(2020-06-01 00:00:00 2020-12-31 23:59:59)
*
* @param date 时间,为空表示当前时间
* @return 时间范围 half year range
*/
public static DateRange getHalfYearRange(Date date) {
DateRange range = new DateRange();
Calendar calendar = Calendar.getInstance();
if (date != null) {
calendar.setTime(date);
}
int current = calendar.get(Calendar.MONTH) + 1;
int month = Calendar.JANUARY;
if (current > HALF_YEAR) {
month = Calendar.JULY;
}
calendar.set(calendar.get(Calendar.YEAR), month, 1, 0, 0, 0);
range.begin = calendar.getTime();
calendar.set(Calendar.MONTH, month + 6);
calendar.set(Calendar.DAY_OF_YEAR, calendar.get(Calendar.DAY_OF_YEAR) - 1);
range.end = setDayToEnd(calendar).getTime();
return range;
}
/**
* 获取指定日期本年的时间范围,如(2020-01-01 00:00:00 2020-12-31 23:59:59)
*
* @param date 时间,为空表示当前时间
* @return 时间范围 year range
*/
public static DateRange getYearRange(Date date) {
DateRange range = new DateRange();
Calendar calendar = Calendar.getInstance();
if (date != null) {
calendar.setTime(date);
}
calendar.set(calendar.get(Calendar.YEAR), Calendar.JANUARY, 1, 0, 0, 0);
range.begin = calendar.getTime();
calendar.set(Calendar.MONTH, 12);
calendar.set(Calendar.DAY_OF_YEAR, calendar.get(Calendar.DAY_OF_YEAR) - 1);
range.end = setDayToEnd(calendar).getTime();
return range;
}
/**
* 获取指定年份本年的日期范围,如(2020-01-01 2020-12-31)
*
* @param year 年份,为空表示当前时间
* @return 时间范围 year range
*/
public static DateRange getYearDateRange(@NonNull String year) {
DateRange range = new DateRange();
String start = year + "-01-01";
String end = year + "-12-31";
range.begin = parseDate(start);
range.end = parseDate(end);
return range;
}
private static Calendar setDayToEnd(Calendar calendar) {
calendar.set(Calendar.HOUR_OF_DAY, 23);
calendar.set(Calendar.MINUTE, 59);
calendar.set(Calendar.SECOND, 59);
return calendar;
}
private static Calendar setDayToBegin(Calendar calendar) {
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
return calendar;
}
/**
* 判断时间date1是否在时间date2之前
*
* @param date1 时间
* @param date2 时间
* @return the boolean
*/
public static boolean isDateBefore(String date1, String date2) {
return Objects.requireNonNull(parseDateTime(date1)).before(parseDateTime(date2));
}
/**
* 验证当前时间是否在指定时间范围内(例如9:00-22:30)
*
* @param startTime 开始时间
* @param endTime 结束时间
* @return bool boolean
*/
public static boolean isRangeTime(String startTime, String endTime) {
boolean isRangeTime = false;
String nowDay = formatDate(new Date());
String start = startTime, end = endTime;
start = nowDay + " " + start + ":00";
end = nowDay + " " + end + ":00";
// 开始时间不在结束时间前,表示时间进行了跨天设置
if (!isDateBefore(start, end)) {
// 跨天设置的时间需要当前时间大于结束时间并且小于开始时间
if (isDateBefore(formatDateTime(new Date()), end)
|| isDateBefore(start, formatDateTime(new Date()))) {
isRangeTime = true;
}
} else {
if (isDateBefore(formatDateTime(new Date()), end)
&& isDateBefore(start, formatDateTime(new Date()))) {
isRangeTime = true;
}
}
return isRangeTime;
}
/**
* utc时间转为date
*
* @param value "2004-08-04T19:09:02.768Z";
* @return date date
*/
public static Date parseUTCDateTimeStamp(String value) {
if (!StringUtils.hasText(value)) {
return null;
}
try {
return UTC_TIME_STAMP_FORMAT.parse(value);
} catch (ParseException e) {
return null;
}
}
/**
* Format utc date time stamp string.
*
* @param date the date
* @return the string
*/
public static String formatUTCDateTimeStamp(Date date) {
return UTC_TIME_STAMP_FORMAT.format(date);
}
/**
* Format utc date time stamp string.
*
* @param dateStr yyyy-MM-dd HH:mm:ss
* @return utc时间 string
*/
public static String formatUTCDateTimeStamp(String dateStr) {
Date d = parseDateTime(dateStr);
return UTC_TIME_STAMP_FORMAT.format(d);
}
/**
* 把日期解析为UTC时间 @param dates the dates
*
* @param dates the dates
* @return the string
*/
public static String formatUTCDate(Date... dates) {
Date date = new Date();
if (dates != null && dates.length > 0) {
date = dates[0];
}
return UTC_FORMAT.format(date);
}
/**
* 把日期解析为GMT时间 @param date the date
*
* @param date the date
* @return the string
*/
public static String formatGMTDate(Date date) {
return GMT_FORMAT.format(date);
}
/**
* 获取今天是一周的星期几
*
* @return day of week
*/
public static int getDayOfWeek() {
Calendar calendar = Calendar.getInstance();
int day = calendar.get(Calendar.DAY_OF_WEEK);
// 星期天对应1,星期一从2开始
if (day == 1) {
return 7;
}
return day - 1;
}
/**
* 获取某年某月的最后一天
*
* @param year 年
* @param month 月
* @return 最后一天 last day of month
*/
public static Date getLastDayOfMonth(int year, int month) {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, year);
cal.set(Calendar.MONTH, month - 1);
cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DATE));
return cal.getTime();
}
/**
* 获取某年某月的第一天
*
* @param year 年
* @param month 月
* @return 第一天 first day of month
*/
public static Date getFirstDayOfMonth(int year, int month) {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, year);
cal.set(Calendar.MONTH, month - 1);
cal.set(Calendar.DAY_OF_MONTH, cal.getMinimum(Calendar.DATE));
return cal.getTime();
}
/**
* 获取当前时间
*
* @return the current time
*/
public static String getCurrentTime() {
Date date = new Date();
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return formatter.format(date);
}
// ============================ 日期操作=============================
/**
* 获取开始日期n天前的日期
*
* @param date 开始日期为空,则初始化一个当前时间
* @param daysToSubtract n
* @return the before days
*/
public static Date getBeforeDays(Date date, @NotNull Long daysToSubtract) {
if (Objects.isNull(date)) {
date = new Date();
}
return localDate2date(date2LocalDate(date).minusDays(daysToSubtract));
}
public static Date beforeDays(Date date, @NotNull Long daysToSubtract) {
if (Objects.isNull(date)) {
date = new Date();
}
return localDateTime2Date(date2LocalDateTime(date).minusDays(daysToSubtract));
}
/**
* 获取开始日期n天后的日期.
*
* @param date 开始日期为空,则初始化一个当前时间
* @param daysToSubtract the days to subtract
* @return the after days
*/
public static Date getAfterDays(Date date, @NotNull Long daysToSubtract) {
if (Objects.isNull(date)) {
date = new Date();
}
return localDate2date(date2LocalDate(date).plusDays(daysToSubtract));
}
/**
* Gets before weeks.
*
* @param date the date
* @param weeksToSubtract the weeks to subtract
* @return the before weeks
*/
public static Date getBeforeWeeks(Date date, @NotNull Long weeksToSubtract) {
if (Objects.isNull(date)) {
date = new Date();
}
return localDate2date(date2LocalDate(date).minusWeeks(weeksToSubtract));
}
/**
* Gets before months.
*
* @param date the date
* @param monthsToSubtract the months to subtract
* @return the before months
*/
public static Date getBeforeMonths(Date date, @NotNull Long monthsToSubtract) {
if (Objects.isNull(date)) {
date = new Date();
}
return localDate2date(date2LocalDate(date).minusMonths(monthsToSubtract));
}
/**
* Gets before years.
*
* @param date the date
* @param yearsToSubtract the years to subtract
* @return the before years
*/
public static Date getBeforeYears(Date date, @NotNull Long yearsToSubtract) {
if (Objects.isNull(date)) {
date = new Date();
}
return localDate2date(date2LocalDate(date).minusMonths(yearsToSubtract));
}
/**
* Date 2 local date time local date time.
*
* @param date the date
* @return the local date time
*/
public static LocalDateTime date2LocalDateTime(@NotNull Date date) {
Instant instant = date.toInstant();
ZoneId zone = ZoneId.systemDefault();
return LocalDateTime.ofInstant(instant, zone);
}
/**
* Date 2 local date local date.
*
* @param date the date
* @return the local date
*/
public static LocalDate date2LocalDate(@NotNull Date date) {
return date2LocalDateTime(date).toLocalDate();
}
/**
* Local date time 2 date date.
*
* @param localDateTime the local date time
* @return the date
*/
public static Date localDateTime2Date(@NotNull LocalDateTime localDateTime) {
ZoneId zone = ZoneId.systemDefault();
Instant instant = localDateTime.atZone(zone).toInstant();
return Date.from(instant);
}
/**
* Local date 2 date date.
*
* @param localDate the local date
* @return the date
*/
public static Date localDate2date(@NotNull LocalDate localDate) {
ZoneId zone = ZoneId.systemDefault();
Instant instant = localDate.atStartOfDay().atZone(zone).toInstant();
return Date.from(instant);
}
/**
* Local date time 2 string string.
*
* @param date the date
* @param fmt the fmt
* @return the string
*/
public static String localDateTime2String(LocalDateTime date, DateTimeFormatter fmt) {
if (Objects.isNull(fmt)) {
fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
}
return date.format(fmt);
}
/**
* 日期间隔.
* Period类表示年、月、日时间间隔
*
* @param startDateInclusive 开始日期
* @param endDateExclusive 结束日期
* @param unit 年/月/日
* @return 间隔 long
*/
public static long between(
LocalDate startDateInclusive, LocalDate endDateExclusive, TemporalUnit unit) {
return Period.between(startDateInclusive, endDateExclusive).get(unit);
}
public static long between(Date start, Date end, TemporalUnit unit) {
return between(date2LocalDateTime(start), date2LocalDateTime(end), unit);
}
/**
* 时间间隔.
* LocalDate、LoclaTime、LocalDateTime等都实现了Temporal接口
* Duration是一个表示秒/纳秒的时间间隔。即Period基于日期值,而Duration基于时间值。 这两个类是为了日期、时间的度量服务的。<br>
*
* @param startInclusive the start inclusive
* @param endExclusive the end exclusive
* @param unit the unit
* @return long long
*/
public static long between(Temporal startInclusive, Temporal endExclusive, TemporalUnit unit) {
return Duration.between(startInclusive, endExclusive).get(unit);
}
/**
* 获取今天最小时间
*
* @return local date time
*/
public static LocalDateTime todayMin() {
return LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
}
/**
* Day min local date time.
*
* @param day the day
* @return the local date time
*/
public static LocalDateTime dayMin(LocalDate day) {
return LocalDateTime.of(day, LocalTime.MIN);
}
/**
* 获取今天最大时间
*
* @return local date time
*/
public static LocalDateTime todayMax() {
return LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
}
/**
* Day max local date time.
*
* @param day the day
* @return the local date time
*/
public static LocalDateTime dayMax(LocalDate day) {
return LocalDateTime.of(day, LocalTime.MAX);
}
/**
* 获取某年某个季度的开始时间
*
* @param year 指定年份
* @param quarter 指定季度(1,2,3,4)
* @return date
*/
public static Date getQuarterStartTime(final int year, final int quarter) {
Calendar c = Calendar.getInstance();
if (quarter <= 0 || quarter > 4) {
return null;
} else if (quarter == 1) {
c.set(year, Calendar.JANUARY, 1);
} else if (quarter == 2) {
c.set(year, Calendar.APRIL, 1);
} else if (quarter == 3) {
c.set(year, Calendar.JULY, 1);
} else {
c.set(year, Calendar.OCTOBER, 1);
}
return c.getTime();
}
/**
* 获取某年某个季度的结束时间
*
* @param year 指定年份
* @param quarter 指定季度(1,2,3,4)
* @return date
*/
public static Date getQuarterEndTime(final int year, final int quarter) {
Calendar c = Calendar.getInstance();
if (quarter <= 0 || quarter > 4) {
return null;
} else if (quarter == 1) {
c.set(year, Calendar.MARCH, 31);
} else if (quarter == 2) {
c.set(year, Calendar.JUNE, 30);
} else if (quarter == 3) {
c.set(year, Calendar.SEPTEMBER, 30);
} else {
c.set(year, Calendar.DECEMBER, 31);
}
return c.getTime();
}
}